提问:哎,怪我太菜。找不到/user/me在哪实现。(这里的9091就是该项目自己的端口)。各位大老指条明路。
基本上所有微服务都是资源服务。
(1)用户微服务
首先,认证服务使用私钥,采用非对称加密算法 生成令牌,资源服务使用公钥 来校验令牌的合法性。 需要配置公钥,并将公钥拷贝到 public.key 文件中,将此文件拷贝到每一个需要的资源服务工程的 classpath 下 ,比如 用户微服务:
解析令牌需要添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
接下来,需要配置每个系统的 Http 请求路径安全控制策略 以及 读取公钥信息识别令牌,对于用户微服务,新建 config 包,提供配置类,@EnableResourceServer 注解用于开启资源校验服务,进行令牌的校验;@EnableGlobalMethodSecurity 注解是全局方法校验:
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)//激活方法上的PreAuthorize注解
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
//公钥
private static final String PUBLIC_KEY = "public.key";
/***
* 定义 JwtTokenStore
* @param jwtAccessTokenConverter
* @return
*/
@Bean
public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
return new JwtTokenStore(jwtAccessTokenConverter);
}
/***
* 定义 JJwtAccessTokenConverter
* @return
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setVerifierKey(getPubKey());
return converter;
}
/**
* 获取非对称加密公钥 Key
* @return 公钥 Key
*/
private String getPubKey() {
Resource resource = new ClassPathResource(PUBLIC_KEY);
try {
InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream());
BufferedReader br = new BufferedReader(inputStreamReader);
return br.lines().collect(Collectors.joining("\n"));
} catch (IOException ioe) {
return null;
}
}
/***
* Http 安全配置,对每个到达系统的 http 请求链接进行校验
* @param http
* @throws Exception
*/
@Override
public void configure(HttpSecurity http) throws Exception {
// 所有请求必须认证通过
http.authorizeRequests()
// 下边的路径放行
.antMatchers(
"/user/add"). //配置地址放行
permitAll()
.anyRequest().
authenticated(); //其他地址需要认证授权
}
}
用户每次访问微服务的时候,需要先申请令牌,令牌申请后,每次将令牌放到 Http headers 中,才能访问微服务。Http headers 中每次 需要 添加一个 Authorization 头信息,头的值为 bearer token("bearer"空格 [token])。
比如,先不携带令牌测试,访问 http://localhost:18087 /user ,不携带令牌,结果如下(因为这时网关获取不到令牌,所以响应 401 错误状态码):
携带正确令牌访问(这时网关可以获取到令牌信息,予以放行):
而且对于 configure 方法中设置的对 user/add 路径放行,可以看到,不需要在 headers 中传 Authorization 参数,确实放行了:
(2)网关微服务
接下来,需要让 OAuthorization 对接网关微服务。用户每次访问微服务的时候,先去 OAuth2.0 服务登录,登录后再访问微服务网关,微服务网关将请求转发给其他微服务处理。
在上一篇博客,搭建了 changgou-user-oauth 微服务,实现了 9001/user/login 的登录功能,在上上一篇博客中,搭建了 changgou-gateway-web 微服务,提供了 AuthorizeFilter 全局过滤器,总的来说,实现了以下功能:
(1)用户登录成功(也就是用户名和密码正确)后,会将令牌信息存入到 cookie 中
(2)用户携带 Cookie 中的令牌 访问微服务网关
(3)微服务网关先获取头文件中的令牌信息,如果 HTTP Headers 中没有 Authorization 令牌信息,则到参数中找,参数中如果没有,则到 Cookie 中找,最后将令牌信息封装到 HTTP Headers ,并调用其他微服务
(4)其他微服务会获取 HTTP Headers 中的 Authorization 令牌信息,然后匹配令牌数据是否能使用公钥解密,如果解密成功说明用户已登录,解密失败,说明用户未登录
修改 changgou-gateway-web 的全局过滤器 AuthorizeFilter 类的逻辑,如果令牌不在 Http Headers 头文件中,需要将 bearer 令牌信息添加到头文件中,而且,现在的令牌是经过私钥生成的,所以是需要用公钥验证,之前的解析JWT的代码不能用啦,先注释掉:
这时访问 http://localhost:8001/api/user ,将通过私钥生成的新令牌放到头文件中,在令牌前面添加 Bearer 令牌:
这里 全局过滤器 AuthorizeFilter的逻辑是,从 Http Headers 中获取到了 Authorization 参数,所以就执行了 chain.filter(exchange)
放行,后续是需要把解析令牌的方法重新提供的。
而且在 AuthorizeFilter 类中,是依次从头文件、参数、Cookie 中获取 Authorization 参数的,所以一次传过 Authorization 参数,没有清空 Cookie 且未过期的话,后续就不需要传了;或者通过传参数的形式,是不需要加 "bearer " 前缀的,因为代码逻辑里会把 token 取出,再加上"bearer " 前缀放到 Http Headers 中: