OAuth2.0-JWT令牌

一、JWT令牌介绍

通过Spring Cloud Security OAuth2的测试我们发现,当资源服务和授权服务不在一起时资源服务使用RemoteTokenServices远程请求授权服务谭政token,如果访问量大将会影响系统的性能。

为了解决上面的问题,可以采用JWT格式即可解决,用户认证通过后会得到一个JWT令牌,JWT令牌中已经包含了用户相关的信息,客户端只需要哦携带JWT访问资源服务,资源服务根据事先约定的算法自行完成令牌校验,无需每次都请求认证服务完成校验。

1、什么是JWT?
JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),它定义了一种简介的、自包含的协议格式,用于在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。JWT可以使用HMAC算法或者RSA的公钥/私钥对签名,防止被篡改。

JWT官网JWT标准

JWT令牌的优点:
1)jwt基于json,非常方便解析。
2)可以在令牌中自定义丰富的内容,易扩展。
3)通过非对称加密算法及数字签名技术,JWT防止篡改,安全性高。
4)资源服务使用JWT可不依赖认证服务即可完成校验授权。

JWT令牌的缺点:
1)JWT令牌较长,占存储空间比较大。

2、JWT令牌结构
通过学习JWT令牌结构为自定义JWT令牌打好基础。
JWT令牌由三部分(Header、Payload、Signature)组成,每部分中间使用点(.)分隔,比如:xxxx.yyyy.zzzz(eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.cThIIoDvwdueQB468K5xDc5633seEFoqwxjF_xSJyQQ)。

JWT令牌组成部分

1)Header
头部包括令牌的类型(即JWT)及使用的哈希算法(如HMACSHA256或RSA),下边是Header部分的内容:

{
  "alg": "HS256",
  "typ": "JWT"
}

2)Payload
第二部分是负载,内容也是一个json对象,它是存放有效信息的地方,它可以存放jwt提供的现成字段,比如:iss(签发者)、exp(过期时间戳)、sub(面向的用户)等,也可自定义字段。此部分不建议存放敏感信息,因为此部分可以解码还原原始内容。将第二部分负载使用Base64Url编码,得到一个字符串就是JWT令牌的第二部分。

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

3)Signature
第三部分是签名,此部分用于防止jwt内容被篡改。这个部分使用Base64Url将前面两部分进行编码,编码后使用点(.)连接组成字符串,最后使用Header中声明的签名算法进行签名。

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  your-256-bit-secret
)

base64UrlEncode(header):jwt令牌的第一部分Header。
base64UrlEncode(payload):jwt令牌的第二部分Payload。
your-256-bit-secret:签名所使用的的秘钥。

二、生成令牌和校验令牌

2.1 生成JWT令牌

在UAA中配置jwt令牌服务,即可实现生成jwt格式的令牌。

1、TokenConfig

/**
 * Token令牌配置
 */
@Configuration
public class TokenConfig {

    /**
     * jwt令牌加密秘钥
     */
    private static final String SIGNING_KEY = "uaa123";

    /**
     * 令牌的存储策略:使用jwt令牌
     * @return TokenStore
     */
    @Bean
    public TokenStore tokenStore() {
        // JWT令牌存储方案
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        // 对称秘钥,资源服务器使用该秘钥来验证
        converter.setSigningKey(SIGNING_KEY);
        return converter;
    }

    /**
     * 临牌的存储策略
     * @return TokenStore
     */
    /*@Bean
    public TokenStore tokenStore() {
        // 使用内存方式存储令牌(普通令牌)
        return new InMemoryTokenStore();
    }*/
}

2、定义JWT令牌服务AuthorizationServer

    /**
     * 令牌管理服务
     * @return
     */
    @Bean
    public AuthorizationServerTokenServices tokenService() {
        DefaultTokenServices service = new DefaultTokenServices();
        // 客户端详情服务
        service.setClientDetailsService(clientDetailsService);
        // 是否产生支持刷新令牌
        service.setSupportRefreshToken(true);
        // 令牌存储策略
        service.setTokenStore(tokenStore);

        // 设置令牌增强
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Collections.singletonList(accessTokenConverter));
        service.setTokenEnhancer(tokenEnhancerChain);

        // 令牌桶默认有效期2小时
        service.setAccessTokenValiditySeconds(7200);
        // 刷新令牌默认有效期3天
        service.setRefreshTokenValiditySeconds(259200);
        return service;
    }
增强JWT令牌
校验JWT令牌

2.2 校验JWT令牌

资源服务需要和授权服务拥有一致的签名、令牌服务等:
1、将授权服务中的TokenConfig类拷贝到资源服务项目中。
2、屏蔽掉资源服务中原来的令牌服务类,不再使用HTTP的方式调用授权服务来校验JWT令牌。

/**
 * Token配置
 */
@Configuration
public class TokenConfig {

    /**
     * jwt令牌加密秘钥,必须和uaa授权服务端保持一致
     */
    private static final String SIGNING_KEY = "uaa123";

    /**
     * 令牌的存储策略:使用jwt令牌
     * @return TokenStore
     */
    @Bean
    public TokenStore tokenStore() {
        // JWT令牌存储方案
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        // 对称秘钥,资源服务器使用该秘钥来验证
        converter.setSigningKey(SIGNING_KEY);
        return converter;
    }

}
使用本地方式校验JWT令牌
带上JWT令牌访问资源,通过校验

推荐阅读更多精彩内容