Spring Security + OAuth2 - 黑马程序员(6. JWT 令牌)学习笔记

上一篇:Spring Security + OAuth2(5. 资源服务搭建与测试)

下一篇:Spring Security + OAuth2(7. Spring Security实现分布式系统授权【从头重写】- UAA)

文章目录

  • 1. JWT 介绍
    • 1.1 什么是JWT
    • 1.2 JWT令牌结构
  • 2. 配置JWT令牌服务
  • 3. 生成jwt令牌
  • 4. 资源服务校验jwt令牌
    • 4.1 TokenConfig
    • 4.2 修改资源服务原来的令牌验证部分
    • 4.3 测试
  • 5. 完善环境配置
    • 5.1 创建表
    • 5.2 配置授权服务
    • 5.3 测试

1. JWT 介绍

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

为了解决上述问题,就有了如下方案:

令牌采用JWT格式即可解决上边的问题

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

1.1 什么是JWT

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

官网:https://jwt.io/
标准:https://tools.ietf.org/html/rfc7519

  • JWT令牌的优点:

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

  • 缺点:

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

1.2 JWT令牌结构

通过学习JWT令牌结构为自定义jwt令牌打好基础。

JWT令牌由三部分组成,每部分中间使用点(.)分隔,比如:xxxxx.yyyyy.zzzzz

  1. Header

    头部包括令牌的类型(即JWT)及使用的哈希算法(如HMAC、SHA256或RSA)

    一个例子如下:

    {
    “alg”: “HS256”,
    “typ”: “JWT”
    }

    将上边的内容使用Base64Url编码,得到一个字符串就是JWT令牌的第一部分。

  2. Payload

    第二部分是负载,内容也是一个json对象,它是存放有效信息的地方,它可以存放jwt提供的现成字段

    比如:iss(签发者),exp(过期时间戳), sub(面向的用户)等,也可自定义字段。

    此部分不建议存放敏感信息,因为此部分可以解码还原原始内容。

    一个例子:

    {
    “sub”: “1234567890”,
    “name”: “456”,
    “admin”: true
    }

    最后将第二部分负载使用Base64Url编码,得到一个字符串就是JWT令牌的第二部分。

  3. Signature

    第三部分是签名,此部分用于防止jwt内容被篡改。

    这个部分使用base64url将前两部分进行编码,编码后使用点(.)连接组成字符串,最后使用header中声明签名算法进行签名。

    一个例子:

    HMACSHA256(
    base64UrlEncode(header) + “.” +
    base64UrlEncode(payload),
    secret)

    其中

    • base64UrlEncode(header):jwt令牌的第一部分。
    • base64UrlEncode(payload):jwt令牌的第二部分。
    • secret:签名所使用的密钥。

2. 配置JWT令牌服务

  • 在uaa中配置jwt令牌服务,即可实现生成jwt格式的令牌
  • 修改 UAA 模块下的 Config 包中配置
  1. TokenConfig

    @Configuration
    public class TokenConfig {
           
    
    	// 对称密钥,资源服务、Uaa 鉴权服务需要一致
        private String SIGNING_KEY = "uaa123";
    
        @Bean
        public TokenStore tokenStore() {
           
            //JWT令牌存储方案
            return new JwtTokenStore(accessTokenConverter());
        }
    
        @Bean
        public JwtAccessTokenConverter accessTokenConverter() {
           
            JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
            converter.setSigningKey(SIGNING_KEY); //对称秘钥,资源服务器使用该秘钥来验证
            return converter;
        }
    }
    
  2. AuthorizationServer -> tokenService

        @Autowired
        private JwtAccessTokenConverter accessTokenConverter;
        //令牌管理服务
        @Bean
        public AuthorizationServerTokenServices tokenService() {
           
            DefaultTokenServices service=new DefaultTokenServices();
            service.setClientDetailsService(clientDetailsService);//客户端详情服务
            service.setSupportRefreshToken(true);//支持刷新令牌
            service.setTokenStore(tokenStore);//令牌存储策略
            //令牌增强
            TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
            tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
            service.setTokenEnhancer(tokenEnhancerChain);
    
            service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
            service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
            return service;
        }
    

3. 生成jwt令牌

Spring Security + OAuth2 - 黑马程序员(6. JWT 令牌)学习笔记_第1张图片

4. 资源服务校验jwt令牌

  • 资源服务需要和授权服务拥有一致的签字、令牌服务等
  • 下面的操作都是在 资源模块 Order 中进行的

4.1 TokenConfig

  • UAA 模块中 Config -> TokenConfig 复制到 Order 模块的 Config 下
    Spring Security + OAuth2 - 黑马程序员(6. JWT 令牌)学习笔记_第2张图片

4.2 修改资源服务原来的令牌验证部分

  • 修改 Config -> ResouceServerConfig

    @Configuration
    @EnableResourceServer
    public class ResouceServerConfig extends ResourceServerConfigurerAdapter {
           
    	
    	// 对称密钥,资源服务、Uaa 鉴权服务需要一致
        public static final String RESOURCE_ID = "res1";
    
        @Autowired
        TokenStore tokenStore;
    
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
           
            resources.resourceId(RESOURCE_ID)//资源 id
                    .tokenStore(tokenStore)
                    .stateless(true);
        }
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
           
    
            http
                    .authorizeRequests()
                    .antMatchers("/**").access("#oauth2.hasScope('all')")//ROLE_ADMIN
                    .and().csrf().disable()
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }
    }
    

4.3 测试

  1. 申请jwt令牌
  2. 使用令牌请求资源
    Spring Security + OAuth2 - 黑马程序员(6. JWT 令牌)学习笔记_第3张图片

补充 :

  • 令牌申请成功可以使用/uaa/oauth/check_token校验令牌的有效性,并查询令牌的内容
  • 例子如下:
    Spring Security + OAuth2 - 黑马程序员(6. JWT 令牌)学习笔记_第4张图片

5. 完善环境配置

截止目前客户端信息和授权码仍然存储在内存中,生产环境中通过会存储在数据库中,下边完善环境的配置:

5.1 创建表

在user_db中创建如下表:

  1. oauth_client_details

    存储用户相关信息

    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;
    
    -- ----------------------------
    -- Table structure for oauth_client_details
    -- ----------------------------
    DROP TABLE IF EXISTS `oauth_client_details`;
    CREATE TABLE `oauth_client_details`  (
      `client_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
      `resource_ids` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
      `client_secret` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
      `scope` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
      `authorized_grant_types` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
      `web_server_redirect_uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
      `authorities` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
      `access_token_validity` int NULL DEFAULT NULL,
      `refresh_token_validity` int NULL DEFAULT NULL,
      `additional_information` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL,
      `create_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
      `archived` tinyint NULL DEFAULT NULL,
      `trusted` tinyint NULL DEFAULT NULL,
      `autoapprove` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
      PRIMARY KEY (`client_id`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Records of oauth_client_details
    -- ----------------------------
    INSERT INTO `oauth_client_details` VALUES ('c1', 'res1', '$2a$10$Dappn49nM8ZjkCO3ASwR/evnHQN3iPH4npP45js0d9Ug3Vm4ui.sq', 'ROLE_ADMIN,ROLE_USER,ROLE_API', 'authorization_code,password,client_credentials,implicit,refresh_token', 'https://www.baidu.com', NULL, 7200, 259200, NULL, '2020-12-13 15:53:10', 0, 0, 'false');
    INSERT INTO `oauth_client_details` VALUES ('c2', 'res2', 'secret', 'ROLE_API', 'authorization_code,password,client_credentials,implicit,refresh_token', 'https://www.baidu.com', NULL, 7200, 259200, NULL, '2020-12-13 15:53:10', 0, 0, 'false');
    
    SET FOREIGN_KEY_CHECKS = 1;
    
  2. oauth_code
    Spring Security OAuth2 用来存储授权码

    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;
    
    -- ----------------------------
    -- Table structure for oauth_code
    -- ----------------------------
    DROP TABLE IF EXISTS `oauth_code`;
    CREATE TABLE `oauth_code`  (
      `code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
      `create_time` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
      `authentication` blob NULL
    ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Records of oauth_code
    -- ----------------------------
    INSERT INTO `oauth_code` VALUES ('Ykj769', '2020-12-13 16:18:25', 0xACED0005737200416F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E70726F76696465722E4F417574683241757468656E7469636174696F6EBD400B02166252130200024C000D73746F7265645265717565737474003C4C6F72672F737072696E676672616D65776F726B2F73656375726974792F6F61757468322F70726F76696465722F4F4175746832526571756573743B4C00127573657241757468656E7469636174696F6E7400324C6F72672F737072696E676672616D65776F726B2F73656375726974792F636F72652F41757468656E7469636174696F6E3B787200476F72672E737072696E676672616D65776F726B2E73656375726974792E61757468656E7469636174696F6E2E416273747261637441757468656E7469636174696F6E546F6B656ED3AA287E6E47640E0200035A000D61757468656E746963617465644C000B617574686F7269746965737400164C6A6176612F7574696C2F436F6C6C656374696F6E3B4C000764657461696C737400124C6A6176612F6C616E672F4F626A6563743B787000737200266A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C654C697374FC0F2531B5EC8E100200014C00046C6973747400104C6A6176612F7574696C2F4C6973743B7872002C6A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C65436F6C6C656374696F6E19420080CB5EF71E0200014C00016371007E00047870737200136A6176612E7574696C2E41727261794C6973747881D21D99C7619D03000149000473697A65787000000002770400000002737200426F72672E737072696E676672616D65776F726B2E73656375726974792E636F72652E617574686F726974792E53696D706C654772616E746564417574686F7269747900000000000001FE0200014C0004726F6C657400124C6A6176612F6C616E672F537472696E673B787074000270317371007E000D74000270337871007E000C707372003A6F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E70726F76696465722E4F41757468325265717565737400000000000000010200075A0008617070726F7665644C000B617574686F72697469657371007E00044C000A657874656E73696F6E7374000F4C6A6176612F7574696C2F4D61703B4C000B726564697265637455726971007E000E4C00077265667265736874003B4C6F72672F737072696E676672616D65776F726B2F73656375726974792F6F61757468322F70726F76696465722F546F6B656E526571756573743B4C000B7265736F7572636549647374000F4C6A6176612F7574696C2F5365743B4C000D726573706F6E7365547970657371007E0016787200386F72672E737072696E676672616D65776F726B2E73656375726974792E6F61757468322E70726F76696465722E426173655265717565737436287A3EA37169BD0200034C0008636C69656E74496471007E000E4C001172657175657374506172616D657465727371007E00144C000573636F706571007E001678707400026331737200256A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C654D6170F1A5A8FE74F507420200014C00016D71007E00147870737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F400000000000067708000000080000000474000D726573706F6E73655F74797065740004636F6465740009636C69656E745F696471007E001974000C72656469726563745F75726C74001568747470733A2F2F7777772E62616964752E636F6D74000573636F706574000A524F4C455F41444D494E78737200256A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C65536574801D92D18F9B80550200007871007E0009737200176A6176612E7574696C2E4C696E6B656448617368536574D86CD75A95DD2A1E020000787200116A6176612E7574696C2E48617368536574BA44859596B8B7340300007870770C000000103F4000000000000171007E002478017371007E0028770C000000103F40000000000000787371007E001C3F40000000000000770800000010000000007874001568747470733A2F2F7777772E62616964752E636F6D707371007E0028770C000000103F4000000000000174000472657331787371007E0028770C000000103F4000000000000171007E001F787372004F6F72672E737072696E676672616D65776F726B2E73656375726974792E61757468656E7469636174696F6E2E557365726E616D6550617373776F726441757468656E7469636174696F6E546F6B656E00000000000001FE0200024C000B63726564656E7469616C7371007E00054C00097072696E636970616C71007E00057871007E0003017371007E00077371007E000B0000000277040000000271007E000F71007E00117871007E0033737200486F72672E737072696E676672616D65776F726B2E73656375726974792E7765622E61757468656E7469636174696F6E2E57656241757468656E7469636174696F6E44657461696C7300000000000001FE0200024C000D72656D6F74654164647265737371007E000E4C000973657373696F6E496471007E000E787074000F303A303A303A303A303A303A303A31740020413742323638324643314142443130313035324444414341353444413343453170737200326F72672E737072696E676672616D65776F726B2E73656375726974792E636F72652E7573657264657461696C732E5573657200000000000001FE0200075A00116163636F756E744E6F6E457870697265645A00106163636F756E744E6F6E4C6F636B65645A001563726564656E7469616C734E6F6E457870697265645A0007656E61626C65644C000B617574686F72697469657371007E00164C000870617373776F726471007E000E4C0008757365726E616D6571007E000E7870010101017371007E0025737200116A6176612E7574696C2E54726565536574DD98509395ED875B0300007870737200466F72672E737072696E676672616D65776F726B2E73656375726974792E636F72652E7573657264657461696C732E5573657224417574686F72697479436F6D70617261746F7200000000000001FE020000787077040000000271007E000F71007E00117870740008746F6D7368696469);
    
    SET FOREIGN_KEY_CHECKS = 1;
    

5.2 配置授权服务

  1. 修改 UAA -> Config -> AuthorizationServer

    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
           
    
        @Autowired
        private TokenStore tokenStore;
    
        @Autowired
        private ClientDetailsService clientDetailsService;
    
        @Autowired
        private AuthorizationCodeServices authorizationCodeServices;
    
        @Autowired
        private AuthenticationManager authenticationManager;
    
        @Autowired
        private JwtAccessTokenConverter accessTokenConverter;
    
        @Autowired
        private PasswordEncoder passwordEncoder;
    
        //将客户端信息存储到数据库
        @Bean
        public ClientDetailsService clientDetailsService(DataSource dataSource) {
           
            ClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
            ((JdbcClientDetailsService) clientDetailsService).setPasswordEncoder(passwordEncoder);
            return clientDetailsService;
        }
    
        //客户端详情服务
        @Override
        public void configure(ClientDetailsServiceConfigurer clients)
                throws Exception {
           
            clients.withClientDetails(clientDetailsService);
        }
    
    
        //令牌管理服务
        @Bean
        public AuthorizationServerTokenServices tokenService() {
           
            DefaultTokenServices service=new DefaultTokenServices();
            service.setClientDetailsService(clientDetailsService);//客户端详情服务
            service.setSupportRefreshToken(true);//支持刷新令牌
            service.setTokenStore(tokenStore);//令牌存储策略
            //令牌增强
            TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
            tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
            service.setTokenEnhancer(tokenEnhancerChain);
    
            service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
            service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
            return service;
        }
    
        @Bean
        public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource) {
           
            return new JdbcAuthorizationCodeServices(dataSource);//设置授权码模式的授权码如何存取
        }
    
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
           
            endpoints
                    .authenticationManager(authenticationManager)//认证管理器
                    .authorizationCodeServices(authorizationCodeServices)//授权码服务
                    .tokenServices(tokenService())//令牌管理服务
                    .allowedTokenEndpointRequestMethods(HttpMethod.POST);
        }
    
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security){
           
            security
                    .tokenKeyAccess("permitAll()")                    //oauth/token_key是公开
                    .checkTokenAccess("permitAll()")                  //oauth/check_token公开
                    .allowFormAuthenticationForClients();				//表单认证(申请令牌)
        }
    }
    

5.3 测试

  1. 测试密码模式申请令牌

    使用密码模式申请令牌,客户端信息需要和数据库中的信息一致。Spring Security + OAuth2 - 黑马程序员(6. JWT 令牌)学习笔记_第5张图片
    Spring Security + OAuth2 - 黑马程序员(6. JWT 令牌)学习笔记_第6张图片

  2. 测试授权码模式

    生成的授权存储到数据库中。
    在这里插入图片描述
    Spring Security + OAuth2 - 黑马程序员(6. JWT 令牌)学习笔记_第7张图片

    Spring Security + OAuth2 - 黑马程序员(6. JWT 令牌)学习笔记_第8张图片
    Spring Security + OAuth2 - 黑马程序员(6. JWT 令牌)学习笔记_第9张图片

测试完成。

你可能感兴趣的:(SpringCloud,security)