SpringCloud-Alibaba微服务实战十七-JWT认证

38次阅读

共计 5731 个字符,预计需要花费 15 分钟才能阅读完成。

概述

在 OAuth2 体系中认证通过后返回的令牌信息分为两大类:不通明令牌(opaque tokens)通明令牌(not opaque tokens)。

不通明令牌 就是一种无可读性的令牌,一般来说就是一段一般的 UUID 字符串。应用不通明令牌会升高零碎性能和可用性,并且减少提早,因为资源服务不晓得这个令牌是什么,代表谁,须要调用认证服务器获取用户信息接口,如下就是咱们在资源服务器中的配置,须要指明认证服务器的接口地址。

security:
  oauth2:
    resource:
      user-info-uri: http://localhost:5000/user/current/get
      id: account-service

通明令牌 的典型代表就是 JWT 了,用户信息保留在 JWT 字符串中,资源服务器本人能够解析令牌不再须要去认证服务器校验令牌。

之前的章节中咱们是应用了不通明令牌 access_token,但思考到在微服务体系中这种中心化的受权服务会成为瓶颈,本章咱们就应用 jwt 来替换之前的 access_token,专(zhuang)业(bi)点就叫去中心化。

jwt 是什么

Json web token (JWT), 是为了在网络应用环境间传递申明而执行的一种基于 JSON 的凋谢规范(RFC 7519)。该 token 被设计为紧凑且平安的,特地实用于分布式站点的单点登录(SSO)场景。JWT 的申明个别被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也能够减少一些额定的其它业务逻辑所必须的申明信息,该 token 也可间接被用于认证,也可被加密。

简略点说就是一种固定格局的字符串,通常是加密的;

它由三局部组成,头部、载荷与签名,这三个局部都是 json 格局。

  • Header 头部:JSON 形式形容 JWT 根本信息,如类型和签名算法。应用 Base64 编码为字符串
  • Payload 载荷: JSON 形式形容 JWT 信息,除了规范定义的,还能够增加自定义的信息。同样应用 Base64 编码为字符串。

    1. iss: 签发者
    2. sub: 用户
    3. aud: 接管方
    4. exp(expires): unix 工夫戳形容的过期工夫
    5. iat(issued at): unix 工夫戳形容的签发工夫
  • Signature 签名: 将前两个字符串用 . 连贯后,应用头部定义的加密算法,利用密钥进行签名,并将签名信息附在最初。

JWT 能够应用对称的加密密钥,但更平安的是应用非对称的密钥,本篇文章应用的是对称加密。

代码批改

数据库

原来应用 access_token 的时候咱们建设了 7 张 oauth2 相干的数据表

应用 jwt 的话只须要在数据库存储一下 client 信息即可,所以咱们只须要保留数据表 oauth_client_details。

其余数据表已不再须要,大家能够删除。

认证服务 AuthorizationServerConfig

  1. 批改 AuthorizationServerConfig 中 TokenStore 的相干配置
@Bean
public TokenStore tokenStore() {//return new JdbcTokenStore(dataSource);
        return new JwtTokenStore(jwtTokenEnhancer());
}


/**
 * JwtAccessTokenConverter
 * TokenEnhancer 的子类,帮忙程序在 JWT 编码的令牌值和 OAuth 身份验证信息之间进行转换。*/
@Bean
public JwtAccessTokenConverter jwtTokenEnhancer(){JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        // 设置对称签名
        converter.setSigningKey("javadaily");
        return converter;
}

之前咱们是将 access_token 存入数据库,应用 jwt 后不再须要存入数据库,所以咱们须要批改存储形式。

jwt 须要应用加密算法对信息签名,这里咱们先应用 对称秘钥(javadaily)来签订咱们的令牌,对称秘钥当然这也认为着资源服务器也须要应用雷同的秘钥。

  1. 批改 configure(AuthorizationServerEndpointsConfigurer endpoints) 办法,配置 jwt
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 如果须要应用 refresh_token 模式则须要注入 userDetailService
        endpoints.authenticationManager(this.authenticationManager)
                        .userDetailsService(userDetailService)
                        .tokenStore(tokenStore())
                        .accessTokenConverter(jwtTokenEnhancer());
}

这里次要是注入 accessTokenConverter,即下面配置的 token 转换器。

通过下面的配置,认证服务器曾经能够帮咱们生成 jwt token 了,这里咱们先应用 Postman 调用一下,看看生成的 jwt token。

从上图看出曾经失常生成 jwt token,咱们能够将生成的 jwt token 拿到 https://jwt.io/ 网站上进行解析。

如果大家对生成 jwt token 的逻辑不是很理解,能够在 DefaultTokenServices#createAccessToken(OAuth2Authentication authentication)JwtAccessTokenConverter#enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication)上打个断点,察看代码执行的成果。

  1. 删除认证服务器提供给资源服务器获取用户信息的接口
/**
 * 获取受权的用户信息
 * @param principal 以后用户
 * @return 受权信息
 */@GetMapping("current/get")
public Principal user(Principal principal){return principal;}

用了通明令牌 jwt token 后资源服务器能够间接解析验证 token,不再须要调用认证服务器接口,所以此处能够间接删除。

  1. 批改 jwt token 有效期(可选)

    jwt token 的默认有效期为 12 小时,refresh token 的有效期为 30 天,如果要批改默认工夫能够注入 DefaultTokenServices 并批改无效工夫。

@Primary
@Bean
public DefaultTokenServices tokenServices(){DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenEnhancer(jwtTokenEnhancer());
        tokenServices.setTokenStore(tokenStore());
        tokenServices.setSupportRefreshToken(true);
        // 设置 token 有效期,默认 12 小时,此处批改为 6 小时   21600
        tokenServices.setAccessTokenValiditySeconds(60 * 60 * 6);
        // 设置 refresh_token 的有效期,默认 30 天,此处批改为 7 天
        tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7);
        return tokenServices;
}

而后在 configure() 办法中增加 tokenServices

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
                // 如果须要应用 refresh_token 模式则须要注入 userDetailService
        endpoints.authenticationManager(this.authenticationManager)
                        .userDetailsService(userDetailService)
            // 注入自定义的 tokenservice,如果不应用自定义的 tokenService 那么就须要将 tokenServce 里的配置移到这里
                        .tokenServices(tokenServices());
}

资源服务器 ResourceServerConfig

  1. 删除资源服务器中配置认证服务器的接口属性 user-info-uri
security:  
    oauth2:  
        resource:  
            id: account-service
  1. 注入 TokenStore 和 JwtAccessTokenConverter
@Bean
public TokenStore tokenStore() {return new JwtTokenStore(jwtTokenEnhancer());
}

@Bean
public JwtAccessTokenConverter jwtTokenEnhancer(){JwtAccessTokenConverter jwtTokenEnhancer = new JwtAccessTokenConverter();
        jwtTokenEnhancer.setSigningKey("javadaily");
        return jwtTokenEnhancer;
}

留神:对称加密算法须要跟认证服务器秘钥保持一致,当然这里能够提取到配置文件中。

  1. 增加 configure(ResourceServerSecurityConfigurer resources) 办法,退出 token 相干配置
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {resources.resourceId(resourceId)
                        .tokenStore(tokenStore());
}

思考题:资源服务器应用 jwt 后从哪校验 token 呢?

给利用增加 @EnableResourceServer 注解后会给 Spring Security 的 FilterChan 增加一个 OAuth2AuthenticationProcessingFilterOAuth2AuthenticationProcessingFilter 会应用 OAuth2AuthenticationManager 来验证 token。

校验逻辑主体代码执行程序如下:

倡议大家在 OAuth2AuthenticationProcessingFilter#doFilter() 处打个断点领会一下校验过程。

网关 SecurityConfig

  1. 创立 ReactiveJwtAuthenticationManager 从 tokenStore 加载 OAuth2AccessToken

    因为原来的 access_token 是存储在数据库中,所以咱们编写了 ReactiveJdbcAuthenticationManager 来从数据库获取 access_token,当初应用 jwt 咱们也须要定义一个 jwt 的相干类 ReactiveJwtAuthenticationManager,代码跟 ReactiveJdbcAuthenticationManager 一样,这里就不再贴出。

  2. 注入 TokenStore 和 JwtAccessTokenConverter
@Bean  
public TokenStore tokenStore() {return new JwtTokenStore(jwtTokenEnhancer());
}

@Bean  
public JwtAccessTokenConverter jwtTokenEnhancer(){JwtAccessTokenConverter jwtTokenEnhancer = new JwtAccessTokenConverter();
        jwtTokenEnhancer.setSigningKey("javadaily");
        return jwtTokenEnhancer;
}

留神:对称加密算法须要跟认证服务器秘钥保持一致,当然这里能够提取到配置文件中。

  1. 批改 SecurityConfig#SecurityWebFilterChain() 办法,替换 ReactiveJdbcAuthenticationManager
ReactiveAuthenticationManager tokenAuthenticationManager 
= new ReactiveJwtAuthenticationManager(tokenStore());

只须要将 tokenStore 传入结构器即可。

测试

大家自行测试。

小结

应用 jwt token 和 access_token 最大的区别就是资源服务器不再须要去认证服务器校验 token,晋升了零碎整体性能,应用 jwt 后我的项目的流程架构如下:

本系列文章目前是第 19 篇,如果大家对之前的文章感兴趣能够移步至 http://javadaily.cn/tags/SpringCloud 查看

应用了 jwt 咱们不仅要看到 jwt 的长处,也要看到它的毛病,这样咱们能力依据理论场景自由选择,上面是 jwt 最大的两个毛病:

  • jwt 是一次性的,一旦 token 被签发,那么在到期工夫之前都是无效的,无奈废除。如果你中途批改了用户权限须要更新信息那就只能从新签发一个 jwt,然而旧的 jwt 还是能够失常应用,应用旧的 jwt 拿到的信息也就是过期的。
  • jwt 蕴含了认证信息,一旦泄露,任何人都能够取得该令牌的所有权限。为了避免盗用,jwt 的无效工夫不应该设置过长。

正文完
 0