一、OAuth2.0
上篇文章我们讲解了OAuth2.0的几种认证模式,前面的讲解Token采用OAuth2.0自带的方式生成的Token,但是这种方式也存在这弊端,通过前面的测试我们发现,当资源服务和授权服务不在一起时资源服务使用RemoteTokenServices 远程请求授权服务验证token,如果访问量较大将会影响系统的性能 。
因此我们可以采用JWT生成令牌,用户认证通过会得到一个JWT令牌,JWT令牌中已经包括了用户相关的信息,客户端只需要携带JWT访问资源服务,资源服务根据事先约定的算法自行完成令牌校验,无需每次都请求认证服务完成授权。
下面是上篇文章的地址:
二、OAuth2.0整合JWT
本篇文章接着上篇文章的内容继续修改。
认证服务修改
先声明JWT的签名模式,这里采用对称加密的方式,密钥设为bxc123
,这个在资源服务器也是这么修改:
@Configuration
public class TokenConfig {
private String SIGNING_KEY = "bxc123";
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(SIGNING_KEY); //对称秘钥,资源服务器使用该秘钥来验证
return converter;
}
}
下面还要修改AuthorizationServer
配制类,主要AuthorizationServerTokenServices
的配制,增加一个TokenEnhancerChain
,将上面声明出的JwtAccessTokenConverter
配制进去。
@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Autowired
private TokenStore tokenStore;
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private AuthorizationCodeServices authorizationCodeServices;
@Autowired
private JwtAccessTokenConverter accessTokenConverter;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()// 使用in‐memory存储
.withClient("c1")// client_id
.secret(new BCryptPasswordEncoder().encode("secret"))
.resourceIds("res1")
.authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")// 该client允许的授权类型 authorization_code,password,refresh_token,implicit,client_credentials
.scopes("all")// 允许的授权范围
.autoApprove(false) //加上验证回调地址
.authorities("admin")
.redirectUris("http://www.baidu.com");
}
//设置授权码模式的授权码如何存取,暂时采用内存方式
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
return new InMemoryAuthorizationCodeServices();
}
@Bean
public AuthorizationServerTokenServices tokenService() {
DefaultTokenServices service = new DefaultTokenServices();
service.setClientDetailsService(clientDetailsService);
service.setSupportRefreshToken(true);
service.setTokenStore(tokenStore);
//令牌增强
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
List<TokenEnhancer> tokenEnhancers = new ArrayList<>();
tokenEnhancers.add(accessTokenConverter);
tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);
service.setTokenEnhancer(tokenEnhancerChain);
service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
return service;
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.authenticationManager(authenticationManager)//认证管理器
.authorizationCodeServices(authorizationCodeServices)//授权码服务
.tokenServices(tokenService())//令牌管理服务
.allowedTokenEndpointRequestMethods(HttpMethod.POST);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()") //oauth/token_key是公开
.checkTokenAccess("permitAll()") //oauth/check_token公开
.allowFormAuthenticationForClients(); //表单认证(申请令牌)
}
}
重启auth认证服务。
资源服务修改
将上面写的TokenConfig
类,覆盖到资源服务中,并修改ResouceServerConfig
配制类,将前面写的远程访问认证服务校验令牌的逻辑,就可以去除掉了:
@Configuration
@EnableResourceServer
public class ResouceServerConfig extends ResourceServerConfigurerAdapter {
public static final String RESOURCE_ID = "res1";
@Autowired
TokenStore tokenStore;
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID)//资源 id
.tokenStore(tokenStore)
.stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasAuthority("admin")
.antMatchers("/common/**").hasAuthority("common")
.and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
重启资源服务。
三、测试
使用密码模式登录系统获取token
,可以看到获取的token已经是Jwt格式的了:
可以在一些Jwt在线解析的网站,解析下JWT:
下面使用token
去访问资源服务接口:
四、JWT内容增强
上面可以看到JWT中默认放置了一些用户的信息,如果我们想要存放其他东西呢,只需使用DefaultOAuth2AccessToken
的setAdditionalInformation
方法,传递一个Map即可放置自定义的数据,其中可以获取用户的信息,也可以再查找其他自定义的信息放置,下面修改认证服务的AuthorizationServer
配制类:
添加TokenEnhancer tokenEnhancer()
方法:
/**
* JWT内容增强
*/
@Bean
public TokenEnhancer tokenEnhancer() {
return (accessToken, authentication) -> {
Map<String, Object> additionalInfo = new HashMap<>();
User principal = (User)authentication.getUserAuthentication().getPrincipal();
String username = principal.getUsername();
additionalInfo.put("three","额外的内容!");
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
};
}
在构建TokenEnhancerChain
对象时,将上面配制的设置进去:
@Bean
public AuthorizationServerTokenServices tokenService() {
DefaultTokenServices service = new DefaultTokenServices();
service.setClientDetailsService(clientDetailsService);
service.setSupportRefreshToken(true);
service.setTokenStore(tokenStore);
//令牌增强
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
List<TokenEnhancer> tokenEnhancers = new ArrayList<>();
//内容增强
tokenEnhancers.add(tokenEnhancer());
tokenEnhancers.add(accessTokenConverter);
tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);
service.setTokenEnhancer(tokenEnhancerChain);
service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
return service;
}
重启认证服务,再次登录,就可以看到增加的信息已经有了:
如果解析JWT,也可以看到内容:
喜欢的小伙伴可以关注我的个人微信公众号,获取更多学习资料!