栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

使用Spring Security 资源服务器来保护Spring Cloud 微服务

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

使用Spring Security 资源服务器来保护Spring Cloud 微服务

我在上一篇对资源服务器进行了简单的阐述

资源服务器改造

以Spring Security实战干货

所需依赖

在Spring Security的基础上,我们需要加入新的依赖来支持OAuth2 Resource Server和JWT。我们需要引入下面几个依赖库:


            org.springframework.boot
            spring-boot-starter-security
        
        
        
            org.springframework.security
            spring-security-oauth2-resource-server
        
       
        
            org.springframework.security
            spring-security-oauth2-jose
        

Spring  Security 5.x 移除了OAuth2.0授权服务器,保留了OAuth2.0资源服务器。

JWT解码

要校验JWT就必须实现对JWT的解码功能,在Spring Security OAuth2 Resource Server模块中,默认提供了解码器,这个解码器需要调用基于:

spring.security.oauth2.resourceserver

配置下的元数据来生成解码配置,这里的配置大部分是调用授权服务器开放的well-known断点,包含了解析验证JWT一系列参数:

  • jwkSetUri  一般是授权服务器提供的获取JWK配置的well-known端点,用来校验JWT Token。

  • jwsAlgorithm 指定jwt使用的算法,默认 RSA-256。

  • issuerUri  获取OAuth2.0 授权服务器元数据的端点。

  • publicKeyLocation 用于解码的公钥路径,作为资源服务器来说将只能持有公钥,不应该持有私钥。

为了实现平滑过渡,默认的配置肯定不能用了,需要定制化一个JWT解码器。接下来我们一步步来实现它。

分离公私钥

资源服务器只能保存公钥,所以需要从之前的jks文件中导出一个公钥。

keytool -export -alias felordcn -keystore   -file <导出cer的全路径>

例如:

keytool -export -alias felordcn -keystore D:keystoresfelordcn.jks  -file d:keystorespublickey.cer

把分离的cer公钥文件放到原来jks文件的路径下面,资源服务器不再保存jks。

自定义jwt解码器

spring-security-oauth2-jose是Spring Security的jose规范依赖。我将根据该类库来实现自定义的JWT解码器。

    @SneakyThrows
    @Bean
    public JwtDecoder jwtDecoder(@Qualifier("delegatingTokenValidator") DelegatingOAuth2TokenValidator validator) {
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        // 从classpath路径读取cer公钥证书来配置解码器
        ClassPathResource resource = new ClassPathResource(this.jwtProperties.getCertInfo().getPublicKeyLocation());
        Certificate certificate = certificateFactory.generateCertificate(resource.getInputStream());
        PublicKey publicKey = certificate.getPublicKey();
        NimbusJwtDecoder nimbusJwtDecoder = NimbusJwtDecoder.withPublicKey((RSAPublicKey) publicKey).build();
        nimbusJwtDecoder.setJwtValidator(validator);
        return nimbusJwtDecoder;
    }

上面的解码器基于我们的公钥证书,同时我还自定义了一些校验策略。不得不说Nimbus的jwt类库比jjwt要好用的多。

自定义资源服务器配置

接下来配置资源服务器。

核心流程和概念

资源服务器其实也就是配置了一个过滤器BearerTokenAuthenticationFilter来拦截并验证Bearer Token。验证通过而且权限符合要求就放行,不通过就不放行。

和之前不太一样的是验证成功后凭据不再是UsernamePasswordAuthenticationToken而是JwtAuthenticationToken:

@Transient
public class JwtAuthenticationToken extends AbstractOAuth2TokenAuthenticationToken {

 private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

 private final String name;

 
 public JwtAuthenticationToken(Jwt jwt) {
  super(jwt);
  this.name = jwt.getSubject();
 }

 
 public JwtAuthenticationToken(Jwt jwt, Collection authorities) {
  super(jwt, authorities);
  this.setAuthenticated(true);
  this.name = jwt.getSubject();
 }

 
 public JwtAuthenticationToken(Jwt jwt, Collection authorities, String name) {
  super(jwt, authorities);
  this.setAuthenticated(true);
  this.name = name;
 }

 @Override
 public Map getTokenAttributes() {
  return this.getToken().getClaims();
 }

 
 @Override
 public String getName() {
  return this.name;
 }

}

这个我们改造的时候要特别注意,尤其是从SecurityContext获取的时候用户凭证信息的时候。

资源管理器配置

从Spring Security 5的某版本开始不需要再集成适配类了,只需要这样就能配置Spring Security,资源管理器也是这样:

@Bean
    SecurityFilterChain jwtSecurityFilterChain(HttpSecurity http) throws Exception {
        return http.authorizeRequests(request -> request.anyRequest()
                        .access("@checker.check(authentication,request)"))
                .exceptionHandling()
                .accessDeniedHandler(new SimpleAccessDeniedHandler())
                .authenticationEntryPoint(new SimpleAuthenticationEntryPoint())
                .and()
                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
                .build();
    }

这里只需要声明使用JWT校验的资源服务器,同时配置好定义的401端点和403处理器即可。这里我加了基于SpEL的动态权限控制,这个再以往都讲过了,这里不再赘述。

JWT个性化解析

从JWT Token中解析数据并生成JwtAuthenticationToken的操作是由JwtAuthenticationConverter来完成的。你可以定制这个转换器来实现一些个性化功能。比如默认情况下解析出来的权限都是带SCOPE_前缀的,而项目用ROLE_,你就可以通过这个类兼容一下老项目。

@Bean
    JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
//        如果不按照规范  解析权限集合Authorities 就需要自定义key
//        jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("scopes");
//        OAuth2 默认前缀是 SCOPE_     Spring Security 是 ROLE_
        jwtGrantedAuthoritiesConverter.setAuthorityPrefix("");
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
        // 设置jwt中用户名的key  默认就是sub  你可以自定义
        jwtAuthenticationConverter.setPrincipalClaimName(JwtClaimNames.SUB);
        return jwtAuthenticationConverter;
    }

这里基本上就改造完成了。你受保护的资源API将由Bearer Token来保护。

在实际生产中建议把资源服务器封装为依赖集成到需要保护资源的的服务中即可。

附加说明

为了测试资源服务器,假设我们有一个颁发令牌的授权服务器。这里简单模拟了一个发令牌的方法用来获取Token:

    @SneakyThrows
    @Test
    public void imitateAuthServer() {

        JwtEncoder jwsEncoder = new NimbusJwsEncoder(jwkSource());

        JwtTokenGenerator jwtTokenGenerator = new JwtTokenGenerator(jwsEncoder);
        OAuth2AccessTokenResponse oAuth2AccessTokenResponse = jwtTokenGenerator.tokenResponse();

        System.out.println("oAuth2AccessTokenResponse = " + oAuth2AccessTokenResponse.getAccessToken().getTokenValue());
    }
    
        @SneakyThrows
    private JWKSource jwkSource() {
        ClassPathResource resource = new ClassPathResource("felordcn.jks");
        KeyStore jks = KeyStore.getInstance("jks");
        String pass = "123456";
        char[] pem = pass.toCharArray();
        jks.load(resource.getInputStream(), pem);

        RSAKey rsaKey = RSAKey.load(jks, "felordcn", pem);

        JWKSet jwkSet = new JWKSet(rsaKey);
        return new ImmutableJWKSet<>(jwkSet);
    }

相关的DEMO已经上传,你可以通过关注公众号“码农小胖哥” 回复 resourceserver获取资源服务器实现的DEMO。

初步认识微服务中的资源服务器


Spring Security中利用JWT退出登录大部分人都写错了配置


JWT应该保存在哪里?


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/336968.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号