资源服务器验证Token的几种方式
在微服务中,除了eureka,config,网关等基本的微服务还有认证服务和资源服务,
上图描述了使用了 OAuth2 的客户端请求验证token的流程,是通过资源服务向认证服务验证token。
过程就是客户端用用户名和密码到认证服务获取token,客户端拿着 token 去各个微服务请求数据接口,
当微服务接到请求后,先要拿着 token 去认证服务校验token 的合法性,如果合法,请求成功接口处理返回数据。
这种方式首先要在认证服务的认证服务配置允许验证token,其是通过调用认证服务的/oauth/check_token接。
方式一资源服务远程调用认证服务验证Token
认证服务需要在AuthorizationServerConfigurerAdapter继承类中配置中增加以下代码,允许资源服务调用校验
token的接口。
@Override
public void configure(final AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients();
security.checkTokenAccess("isAuthenticated()");
}
资源服务的配置文件如下,http://localhost:3000/oauth/check_token是接到请求向认证服务校验的接口。
security:
oauth2:
client:
clientId: client
clientSecret: secret
access-token-uri: http://localhost:3000/oauth/token
user-authorization-uri: http://localhost:3000/oauth/authorize
resource:
user-info-uri: http://localhost:3000/user
prefer-token-info: false
#jwt:
# key-uri: http://localhost:32002/oauth/token_key
id: cpms-resource-user
authorization:
check-token-access: http://localhost:3000/oauth/check_token
资源服务需要在ResourceServerConfigurerAdapter继承类中新增以下代码,配置校验token的地址
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(final HttpSecurity http) throws Exception {
http.headers().frameOptions().disable().and().csrf().disable().exceptionHandling()
.authenticationEntryPoint(
(request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED)
.antMatchers("/user/register", "/v2/api-docs", "/swagger-ui/**")
.permitAll().anyRequest().authenticated();
}
@Override
public void configure(final ResourceServerSecurityConfigurer resources) throws Exception {
super.configure(resources);
resources.tokenServices(tokenService());
}
//资源服务令牌解析服务
@Bean
public ResourceServerTokenServices tokenService() {
//使用远程服务请求授权服务器校验token,必须指定校验token 的url、client_id,client_secret
final RemoteTokenServices service = new RemoteTokenServices();
service.setCheckTokenEndpointUrl("http://localhost:3000/oauth/check_token");
service.setClientId("client");
service.setClientSecret("secret");
return service;
}
}
以上方式每次请求资源服务都需要向认证服务校验,增加了网络开销。
方式二资源服务启动调用/oauth/token_key接口初始化tokenstore(一般默认方式)
另外一种校验方式就是资源服务自己校验token,也就是资源服务拿到token后,根据已知的算法和签名密钥去校验token,这种方式减少了网络开销。认证服务需要在AuthorizationServerConfigurerAdapter集成类中配置中修改以下代码,允许资源服务访问认证服务获取token算法和签名密钥的接口。
@Override
public void configure(final AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()");
security.allowFormAuthenticationForClients();
}
因此资源服务的配置需要修改,将token_key接口的注释打开。
security:
oauth2:
client:
clientId: client
clientSecret: secret
access-token-uri: http://localhost:3000/oauth/token
user-authorization-uri: http://localhost:3000/oauth/authorize
resource:
user-info-uri: http://localhost:3000/user
prefer-token-info: false
jwt:
key-uri: http://localhost:32002/oauth/token_key
id: cpms-resource-user
authorization:
check-token-access: http://localhost:3000/oauth/check_token
修改资源服务的ResourceServerConfigurerAdapter继承类,只需要以下代码。
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(final HttpSecurity http) throws Exception {
http.headers().frameOptions().disable().and().csrf().disable().exceptionHandling()
.authenticationEntryPoint( (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED)
.antMatchers("/user/register", "/v2/api-docs", "/swagger-ui/**") .permitAll().anyRequest().authenticated();
}
}
资源服务启动时会调用/oauth/token_key接口,获取token加密算法和签名密钥,拿到这些信息后初始化tokenstore等Bean。完成启动后客户端带token的请求到资源服务器,资源服务器会通过相关过滤器进行token校验,这里就不展开了。这里资源服务启动需要依赖于认证服务,认证服务启动后才能启用资源服务,否则资源服务启动不了。资源服务启动后认证服务打印的日志如下图所示。
接口返回的token算法和签名密钥如下图所示
方式三资源服务配置token相关的Bean
方式二是资源服务自己校验token,依赖于认证服务才能启动,方式三其实和方式二一样,同样是自己校验token,但是区别在于启动时不需要去认证服务调用/oauth/token_key接口,不依赖域认证服务启动。只需要针对资源服务配置做以下修改,配置TokenStore的Bean。
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(final HttpSecurity http) throws Exception {
http.headers().frameOptions().disable().and().csrf().disable().exceptionHandling()
.authenticationEntryPoint( (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED)
.antMatchers("/user/register", "/v2/api-docs", "/swagger-ui/**") .permitAll().anyRequest().authenticated();
}
@Bean
public TokenStore jwtTokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
final JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setAccessTokenConverter(new CustomAccessTokenConverter());
converter.setSigningKey("和认证服务一样的signkey");
return converter;
}
@Bean
@ConditionalOnBean(TokenEnhancer.class)
public TokenEnhancer jwtTokenEnhancer() {
return new TokenJwtEnhancer();
}
}
以上就是资源服务验证token的三种方式,第一种方式是远程验证token,每个请求到来都要拿着token去请求认证服务进行验证,增加了网络开销。第二种方式是配置文件中配置/oauth/token_key接口,服务启动时调用该接口获取token算法和签名密钥,完成TokenStore初始化,该方式不需要任何代码配置,但是依赖认证服务启动。方式三在资源服务ResourceServerConfig配置了token相关的Bean,在服务启动时完成初始化Bean,不需要去调用认证服务获取相关算法和签名密钥,其原理和方式二基本一致,但启动不依赖于认证服务。