Spring Security + OAuth2.0 构建微服务统一认证解决方案(二)

搭建过程可以分为以下几步

  1. 构建简单的Spring Security + OAuth2.0 认证服务
  2. 优化认证服务(使用JWT技术加强token,自定义auth接口以及返回结果)
  3. 配置gateway服务完成简单鉴权功能
  4. 优化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 构建微服务统一认证解决方案(三)

github 仓库

posted @ 2021-11-12 18:09  BuptWade  阅读(1260)  评论(0编辑  收藏  举报