Spring Security + OAuth2.0 构建微服务统一认证解决方案(二)
搭建过程可以分为以下几步
- 构建简单的Spring Security + OAuth2.0 认证服务
- 优化认证服务(使用JWT技术加强token,自定义auth接口以及返回结果)
- 配置gateway服务完成简单鉴权功能
- 优化gateway配置(添加复杂鉴权逻辑等等)
(二)优化认证服务
在上一篇中已经介绍了如何搭建简单的认证服务。但在该简单框架中,token虽然可生成可刷新,但是它并没有和用户信息挂钩,无法用于验证。
在本篇中将会使用JWT加强token,验证时可以直接解析token获取其中信息。
一. 使用JWT加强Token
使用keytool 生成RSA证书jwt.jks,复制到resource目录下
mac系统下,使用该命令查看Java安装目录
/usr/libexec/java_home -V
进入Java安装目录下的bin目录,生成 RSA 证书,生成过程中需要填写密码等一系列信息
keytool -genkey -alias jwt -keyalg RSA -keystore jwt.jks
将生成的文件复制到resource目录下
在之前基础上,修改OAuth配置文件 OAuth2ServerConfig,读取RSA证书信息,并配置好转换JWT Token
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userService)
// 设置token转换器
.accessTokenConverter(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setKeyPair(keyPair());
return converter;
}
@Bean
public KeyPair keyPair() {
// 这里的password需要与之前创建时输入的一致,否则会无法读取导致服务启动失败
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "password".toCharArray());
return keyStoreKeyFactory.getKeyPair("jwt");
}
再次启动服务进行测试,发现生成的Token已经变为了JWT Token
使用在线解析可以看到JWT Token中包含的内容
二. JWT中添加自定义字段
生成JWT Token后,可能需要进行扩展,在其中添加一些自定义字段。
实现TokenEnhancer接口进行内容增强,例如在Token中添加userId字段.
@Service
public class JwtTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
Map<String, Object> additionalInfo = new HashMap<>();
SecurityUser user = (SecurityUser) oAuth2Authentication.getPrincipal();
additionalInfo.put("userId", user.getId());
((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(additionalInfo);
return oAuth2AccessToken;
}
}
在config中配置该Enhancer
@Autowired
private JwtTokenEnhancer jwtTokenEnhancer;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userService)
.accessTokenConverter(accessTokenConverter());
// 将两个增强器连起来
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtTokenEnhancer, accessTokenConverter()));
endpoints.tokenEnhancer(tokenEnhancerChain);
}
启动服务进行测试,发现生成的JWT Token带上了自定义的信息
三. 自定义oauth/token 返回数据结构
SpringSecurity提供的auth接口如下
故只需要自定义认证接口,在接口内调用该函数,将返回的OAuth2AccessToken包装为自定义的数据结构即可
具体地,定义返回数据结构
@Data
@Builder
public class TokenDTO {
private String token;
private String refreshToken;
private String tokenHead;
}
自定义认证接口如下
@RestController
@RequestMapping("/oauth")
public class AuthController {
@Autowired
private TokenEndpoint tokenEndpoint;
@PostMapping(value = "/token")
public CommonResult<TokenDTO> postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
TokenDTO tokenDTO = TokenDTO.builder().token(oAuth2AccessToken.getValue())
.refreshToken(oAuth2AccessToken.getRefreshToken().getValue())
.tokenHead("Bearer ").build();
return CommonResult.success(tokenDTO);
}
}
启动服务进行测试,返回了自定义的数据结构
Spring Security + OAuth2.0 构建微服务统一认证解决方案(一)
Spring Security + OAuth2.0 构建微服务统一认证解决方案(三)