分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法

目录

  • 文章
  • 一、简介
  • 二、JWT
  • 三、代码实现——JWT格式令牌
    • 3.1 POM依赖
    • 3.2 修改授权服务器
      • 修改TokenStore
      • 修改令牌管理服务tokenServices()
    • 3.3 修改资源服务器
      • TokenStore
  • 四、测试——JWT令牌申请校验
    • 4.1 原来申请的普通令牌
    • 4.2 申请的JWT令牌
    • 4.3 请求资源服务器认证
  • 五、代码实现——数据库存储客户端详情、授权码
    • 5.1 建表SQL
    • 5.2 数据库存储客户端详情
    • 5.3 数据库存储授权码
  • 六、测试——数据库存储客户端详情、授权码
    • 6.1 申请令牌
    • 6.2 校验令牌
    • 申请授权码
  • 七、JWT RSA非对称加密
    • 7.1 生成证书
    • 7.2 授权服务器修改TokenStore
    • 7.3 资源服务器修改TokenStore
    • 7.4 授权服务器使用私钥加密申请令牌
    • 7.5 资源服务器使用公钥解密JWT令牌
    • 7.6 用不同证书的公钥进行解密
  • 八、使用RedisTokenStore颁发令牌

文章

分布式系统认证解决方案SpringSecurityOAuth2.0(一)认证授权
分布式系统认证解决方案SpringSecurityOAuth2.0(二)分布式系统认证流程分析与实现
分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法
分布式系统认证解决方案SpringSecurityOAuth2.0(四)整合网关认证授权

一、简介

使用JWT令牌格式的话,用户认证通过会得到一个JWT令牌,JWT令牌中已经包括了用户相关的信息,客户端只需要携带JWT访问资源服务,资源服务根据事先约定的算法自行完成令牌校验,无需每次都请求认证服务器完成授权,节省开销。

二、JWT

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

一个JWT实际上就是一个字符串,它由三部分组成:

  1. 头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法
  2. 载荷就是存放有效信息的地方,比如登录的用户名,登录时间,登录过期时间,自定义数据(角色信息等等)
  3. 签证信息,这个签证信息由三部分组成:
    header (base64加密后的)
    payload (base64加密后的)
    secret // 盐,也是一个秘钥
    这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密。

注意:
secret是保存在服务器端的,secret就是用来进行JWT的签发和验证,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发JWT了。

JWT介绍及使用

三、代码实现——JWT格式令牌

3.1 POM依赖

<!-- spring-security-jwt -->
     <dependency>
          <groupId>org.springframework.security</groupId>
          <artifactId>spring-security-jwt</artifactId>
          <version>1.1.0.RELEASE</version>
      </dependency>

3.2 修改授权服务器

修改TokenStore

分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第1张图片

修改令牌管理服务tokenServices()

分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第2张图片

3.3 修改资源服务器

TokenStore

将认证服务器中的TokenStore拷到资源服务中:
分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第3张图片
配置到ResourceServer中:
分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第4张图片
注意:token秘钥需要与认证服务器中的一致。

四、测试——JWT令牌申请校验

4.1 原来申请的普通令牌

分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第5张图片

4.2 申请的JWT令牌

分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第6张图片

4.3 请求资源服务器认证

分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第7张图片

五、代码实现——数据库存储客户端详情、授权码

5.1 建表SQL

CREATE TABLE `oauth_client_details` (
  `client_id` varchar(128) NOT NULL COMMENT '客户端ID',
  `resource_ids` varchar(256) DEFAULT NULL COMMENT '资源ID集合,多个资源时用英文逗号分隔',
  `client_secret` varchar(256) DEFAULT NULL COMMENT '客户端密匙',
  `scope` varchar(256) DEFAULT NULL COMMENT '客户端申请的权限范围',
  `authorized_grant_types` varchar(256) DEFAULT NULL COMMENT '客户端支持的grant_type',
  `web_server_redirect_uri` varchar(256) DEFAULT NULL COMMENT '重定向URI',
  `authorities` varchar(256) DEFAULT NULL COMMENT '客户端所拥有的SpringSecurity的权限值,多个用英文逗号分隔',
  `access_token_validity` int(11) DEFAULT NULL COMMENT '访问令牌有效时间值(单位秒)',
  `refresh_token_validity` int(11) DEFAULT NULL COMMENT '更新令牌有效时间值(单位秒)',
  `additional_information` varchar(4096) DEFAULT NULL COMMENT '预留字段',
  `autoapprove` varchar(256) DEFAULT NULL COMMENT '用户是否自动Approval操作',
  PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端信息';

INSERT INTO `seata`.`oauth_client_details`(`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('c1', 'all', '$2a$10$n/wCt3PAUiFRwWmygwphaODTGf4nZku6UJQh1Lyy8YKmi1cfGtrDW', 'ROLE_ADMIN,ROLE_USER,ROLE_API,ALL', 'authorization_code,password,client_credentials,implicit,refresh_token', 'http://www.baidu.com', NULL, NULL, NULL, NULL, 'false');
INSERT INTO `seata`.`oauth_client_details`(`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('c2', 'all', '$2a$10$4ITes3sAlqV9B2lUB3nJA.9cd16aGC8cyEP9VNpa6pr8Ag9CqzyJy', 'ROLE_ADMIN,ROLE_USER,ROLE_API,ALL', 'authorization_code,password,client_credentials,implicit,refresh_token', 'http://www.baidu.com', NULL, NULL, NULL, NULL, 'false');

CREATE TABLE `oauth_code` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `CODE` varchar(256) DEFAULT NULL COMMENT '授权码(未加密)',
  `authentication` blob COMMENT 'AuthorizationRequestHolder.java对象序列化后的二进制数据',
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='授权码';



将原来我们保存在内存中的客户端详细信息存储到数据库中:
注意:客户端秘钥是经过我们BCrypt加密的。

分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第8张图片

5.2 数据库存储客户端详情

分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第9张图片
分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第10张图片
在这里插入图片描述
在这里插入图片描述

5.3 数据库存储授权码

分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第11张图片

六、测试——数据库存储客户端详情、授权码

6.1 申请令牌

分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第12张图片

6.2 校验令牌

分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第13张图片

申请授权码

GET请求申请授权码:http://localhost:8081/oauth2/oauth/authorize?client_id=c1&client_secret=order&response_type=code&scope=ALL&redirect_uri=http://www.baidu.com
注意:scope范围要是数据库中存储的。
分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第14张图片
可以看到授权码存储在数据库中:
在这里插入图片描述

七、JWT RSA非对称加密

使用RSA非对称加密方式生成公钥私钥

7.1 生成证书

创建一个目录,存放jks文件,在该目录下执行:keytool -genkeypair -alias com.lsh.rsa -keyalg RSA -keypass rsapassword -keystore rsa_first.jks -storepass rsapassword

  1. -alias:密钥的别名 : com.lsh.rsa
  2. -keyalg:使用的hash算法 : RSA
  3. -keypass:密钥的访问密码 : rsapassword
  4. -keystore:密钥库文件名,rsa_first.jks保存了生成的证书
  5. -storepass:密钥库的访问密码: rsapassword 查看证书时用
    分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第15张图片
    分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第16张图片

查看证书:keytool -list -keystore rsa_first.jks
输入创建证书时的秘钥库访问密码。

分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第17张图片

需要将jks文件导出,才能获得我们需要的公钥/私钥信息。
导出证书是用openssl工具,openssl是一个加解密工具包,来导出公钥信息:
分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第18张图片

7.2 授权服务器修改TokenStore

将生成的jks证书拷贝到resources目录下:
分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第19张图片

修改原来的对称加密的accessTokenConverter()方法:

/**创建jks文件时的别名-alias ims.abc.com*/
    public String rsaAlias = "rsafirst";
    /**创建jks文件时的访问密码-keypass */
    public String rsaPassword = "rsapassword";
    /**RSA证书  */
    public String certificateFileName = "rsa_first.jks";

    /**
     * 使用JWT存储令牌   RSA非对称加密
     * @return
     */
    @Bean
    public JwtAccessTokenConverter accessTokenConverter(){
        //使用JWT存储令牌
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        //创建 密钥存储密钥工厂
        ClassPathResource classPathResource = new ClassPathResource(certificateFileName);

        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(classPathResource,rsaPassword.toCharArray());

        //设置密钥对(私钥) 此处传入的是创建jks文件时的别名-alias 和 秘钥库访问密码
        jwtAccessTokenConverter.setKeyPair(keyStoreKeyFactory.getKeyPair(rsaAlias));

        //用户身份验证转换器 给Token中加入额外的扩展数据
        DefaultAccessTokenConverter accessTokenConverter = (DefaultAccessTokenConverter) jwtAccessTokenConverter.getAccessTokenConverter();
        accessTokenConverter.setUserTokenConverter(customUserAuthenticationConverter);
        //在资源服务 验证JWT时 使用公钥进行解密 如:Order服务
        return jwtAccessTokenConverter;
    }

/**
 * @author :LiuShihao
 * @date :Created in 2021/8/24 9:03 上午
 * @desc :用户身份验证转换器,简单理解就是可以给Token中加入额外的扩展数据
 */
@Component
public class CustomUserAuthenticationConverter extends DefaultUserAuthenticationConverter {
    @Override
    public Map<String, ?> convertUserAuthentication(Authentication authentication) {
        LinkedHashMap response = new LinkedHashMap();
        String name = authentication.getName();
        response.put("user_name", name);
        response.put("user_age",18);
        //根据自己的情况增加 扩展数据
        if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
            //权限
            response.put("authorities", AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
        }
        return response;
    }
}

7.3 资源服务器修改TokenStore

分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第20张图片
在这里插入图片描述

修改原来使用对称加密的accessTokenConverter()方法,使用公钥解密:

 	/**存放公钥的文件名 */
    public String first_public = "first_public.txt";
    public String second_public = "second_public.txt";

    /**
     * 使用JWT存储令牌   RSA非对称加密
     *
     * 使用私钥 在认证服务器进行加密
     * 使用公钥 在资源服务器解析JWT时解密
     * @return
     */
    @Bean
    public JwtAccessTokenConverter accessTokenConverter(){
        //使用JWT存储令牌 (普通令牌)
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();

        //公钥(读取public.txt的公钥信息)
        Resource resource = new ClassPathResource(publicFileName);
        String publicKey = null;

        try {
            publicKey = inputStream2String(resource.getInputStream());
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
        //设置验证秘钥(公钥)
        jwtAccessTokenConverter.setVerifierKey(publicKey);

        return jwtAccessTokenConverter;
    }
    public String inputStream2String(InputStream is) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(is));
        StringBuffer buffer = new StringBuffer();
        String line = "";
        while ((line = in.readLine()) != null) {
            buffer.append(line);
        }
        return buffer.toString();
    }

7.4 授权服务器使用私钥加密申请令牌

分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第21张图片

我们发现令牌的长度变得很长。

调用授权服务器校验令牌:
分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第22张图片
可以看到我们自定义内容也解析出来了。

7.5 资源服务器使用公钥解密JWT令牌

分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第23张图片

7.6 用不同证书的公钥进行解密

使用RSA算法生成了两个jks证书:
在这里插入图片描述
在授权服务器我们使用rsa_first.jks证书进行加密:
分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第24张图片
在资源服务器使用rsa_second.jks证书的公钥进行解密:
分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第25张图片

分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第26张图片
可以看到使用不同证书的公钥进行解密是不行的。

八、使用RedisTokenStore颁发令牌

分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第27张图片
分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第28张图片
在Redis中输入get access:token值可以看到存入的令牌信息:

分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第29张图片

分布式系统认证解决方案SpringSecurityOAuth2.0(三)资源服务器使用Redis令牌、JWT令牌认证及RSA非对称加密算法_第30张图片
注意:如果授权服务器的TokenStore的类型改了,资源服务器也需要统一。

你可能感兴趣的:(分布式,SpringCloud,jwt,java,SpringCloud,新星计划)