• Spring Security OAuth2:整合jwt


    接着上一篇博客:https://www.cnblogs.com/wwjj4811/p/14505081.html

    JWT介绍

    JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),它定义了一种紧凑且独立的方式,用于在各方之间作为JSON对象安全地传输信息。此信息可以通过数字签名进行验证和信任。JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名,防止被篡改

    构成

    JWT 有三部分构成:头部、有效载荷、签名

    • 头部:包含令牌的类型(JWT)与加密的签名算法(如 SHA256 或 ES256),Base64编码后加入第一部分

    • 有效载荷:通俗一点讲就是token中需要携带的信息都将存于此部分,比如:用户id、权限标识等信息。

      注:该部分信息任何人都可以读出来,所以添加的信息需要加密才会保证信息的安全性

    • 签名:用于防止 JWT 内容被篡改, 会将头部和有效载荷分别进行 Base64编码,编码后用.连接组成新的字符串,然后再使用头部声明的签名算法进行签名。在具有秘钥的情况下,可以验证JWT的准确性,是否被篡改

    认证服务器-对称加密 JWT 令牌

    对称加密就是同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。

    JWT 管理令牌

    1.重构 com.wj.oauth2.server.config.TokenConfig 将令牌存储方式切换为 JWT

    @Configuration
    public class TokenConfig {
    
        @Bean
        public TokenStore tokenStore(){
            //jwt管理令牌
            return new JwtTokenStore(jwtAccessTokenConverter());
        }
    
        // JWT 签名秘钥
        private static final String SIGNING_KEY = "wj-key";
    
        @Bean
        public JwtAccessTokenConverter jwtAccessTokenConverter(){
            JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
            jwtAccessTokenConverter.setSigningKey(SIGNING_KEY);
            return jwtAccessTokenConverter;
        }
    }
    

    JWT转换器添加到令牌端点

    修改AuthorizationServerConfig类:

        @Autowired
        private JwtAccessTokenConverter jwtAccessTokenConverter;
    
        /**
         * 重写父类的方法
         */
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            //密码模式需要设置此认证管理器
            endpoints.authenticationManager(authenticationManager);
            // 刷新令牌获取新令牌时需要
            endpoints.userDetailsService(customUserDetailsService);
            //设置token存储策略
            endpoints.tokenStore(tokenStore).accessTokenConverter(jwtAccessTokenConverter);
            //授权码管理策略,针对授权码模式有效,会将授权码放到 auth_code 表,授权后就会删除它
            endpoints.authorizationCodeServices(authorizationCodeServices());
        }
    

    测试

    使用密码授权模式获取access_token,发现access_token已经响应为jwt令牌

    image-20210309142625908

    检查 JWT 令牌:http://localhost:8090/auth/oauth/check_token,已经包含了用户基本信息

    image-20210309143238019

    资源服务器-对称加密 JWT 令牌

    在 JwtAccessTokenConverter 中使用了一个对称密钥来签署我们的令牌,意味着我们需要为资源服务器使用同样的密钥来验证签名合法性。

    JWT 管理令牌

    这部分与认证服务器令牌配置是一样的, 将 JWT 令牌部分拷贝到cloud-oauth2-resource-product中的com.wj.oauth2.resource.config 包下即可.

    @Configuration
    public class TokenConfig {
    
        @Bean
        public TokenStore tokenStore(){
            //jwt管理令牌
            return new JwtTokenStore(jwtAccessTokenConverter());
        }
    
        // JWT 签名秘钥
        private static final String SIGNING_KEY = "wj-key";
    
        @Bean
        public JwtAccessTokenConverter jwtAccessTokenConverter(){
            JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
            jwtAccessTokenConverter.setSigningKey(SIGNING_KEY);
            return jwtAccessTokenConverter;
        }
    
    }
    

    资源服务器校验 JWT 令牌

    修改ResourceServerConfig配置类:注释掉tokenService方法,并设置resources的tokenStore,就会自动本地校验jwt

    @Configuration
    // 标识为资源服务器, 所有发往当前服务的请求,都会去请求头里找token,找不到或验证不通过不允许访问
    @EnableResourceServer
    //开启方法级别权限控制
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    
        //配置当前资源服务器的ID
        private static final String RESOURCE_ID = "product-server";
    
        @Autowired
        private TokenStore tokenStore;
    
        /**当前资源服务器的一些配置, 如资源服务器ID **/
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            // 配置当前资源服务器的ID, 会在认证服务器验证(客户端表的resources配置了就可以访问这个服务)
            resources.resourceId(RESOURCE_ID)
                // 实现令牌服务, ResourceServerTokenServices实例
                .tokenStore(tokenStore);
        }
    
    /*    @Bean
        public ResourceServerTokenServices tokenService() {
            // 资源服务器去远程认证服务器验证 token 是否有效
            RemoteTokenServices service = new RemoteTokenServices();
            // 请求认证服务器验证URL,注意:默认这个端点是拒绝访问的,要设置认证后可访问
            service.setCheckTokenEndpointUrl("http://localhost:8090/auth/oauth/check_token");
            // 在认证服务器配置的客户端id
            service.setClientId("wj-pc");
            // 在认证服务器配置的客户端密码
            service.setClientSecret("wj-secret");
            return service;
        }*/
    
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.sessionManagement()
                    //不创建session
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    //资源授权规则
                    .authorizeRequests()
                    .antMatchers("/product/**").hasAuthority("product")
                    //所有的请求对应访问的用户都要有all范围的权限
                    .antMatchers("/**").access("#oauth2.hasScope('all')");
        }
    }
    

    测试

    密码认证模式先获取access_token,再通过 JWT令牌查询获取商品资源

    image-20210309145538851

    为了安全性, JWT令牌每次请求获取令牌都是响应一个新令牌,因为里面已经包含用户信息, 而之前的是令牌在有效时间里每次请求都是响应一样的令牌。

    认证服务器-非对称加密 JWT 令牌

    非对称加密算法需要两个密钥:公开密钥(publickey:简称公钥)和私有密钥(privatekey:简称私钥)。公钥与私钥是一对,如果用私钥对数据进行加密,只有用对应的公钥才能解密。

    生成密钥证书

    公私密钥可以用jdk的命令keytool生成:别名为 oauth2,秘钥算法为 RSA,秘钥口令为 oauth2,秘钥库(文件)名称为 oauth2.jks,秘钥库(文件)口令为 oauth2

    keytool -genkeypair -alias oauth2 -keyalg RSA -keypass oauth2 -keystore oauth2.jks -storepass oauth2
    

    image-20210309150042457

    生成后,在命令执行命令的所在目录下会有一个oauth2.jks文件

    image-20210309150157623

    将该文件移动到认证服务的resources目录下:

    image-20210309150301481

    非对称加密 JWT 令牌

    修改认证服务的TokenConfig类:

    @Configuration
    public class TokenConfig {
    
        @Bean
        public TokenStore tokenStore(){
            //return new JdbcTokenStore(dataSource);
            //jwt管理令牌
            return new JwtTokenStore(jwtAccessTokenConverter());
        }
    
        @Bean
        public JwtAccessTokenConverter jwtAccessTokenConverter(){
            JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
            KeyStoreKeyFactory keyFactory = new KeyStoreKeyFactory(new ClassPathResource("oauth2.jks"), "oauth2".toCharArray());
            jwtAccessTokenConverter.setKeyPair(keyFactory.getKeyPair("oauth2"));
            return jwtAccessTokenConverter;
        }
    
    }
    

    pom文件

    修改认证服务的pom.xml

        <build>
            <resources>
                <resource>
                    <!-- 防止JKS被maven错误解析 -->
                    <directory>src/main/resources</directory>
                    <filtering>false</filtering>
                </resource>
            </resources>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>2.2.4.RELEASE</version>
                    <configuration>
                        <mainClass>com.wj.oauth2.AuthServerApplication</mainClass>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    

    因为有可能jks文件无法正确被maven解析,导致项目启动报错。

    资源服务器-非对称加密 JWT 令牌

    根据密钥文件获取公钥

    OpenSSL 是一个加解密工具包,可以使用 OpenSSL 来获取公钥,下载网址:http://slproweb.com/products/Win32OpenSSL.html

    安装完成后,配置openssl环境变量,即安装目录in

    image-20210309151209245

    获取公钥

    进入oauth2.jks所在目录执行命令:

    keytool -list -rfc --keystore oauth2.jks | openssl x509 -inform pem -pubkey
    

    image-20210309151410165

    复制出控制台打印出的内容到public.txt,并放到资源服务器的resources目录下(需要复制出public key的内容)

    image-20210309152457825

    非对称加密 JWT 令牌

    修改资源服务器的TokenConfig类:

    @Slf4j
    @Configuration
    public class TokenConfig {
    
        @Bean
        public TokenStore tokenStore(){
            //jwt管理令牌
            return new JwtTokenStore(jwtAccessTokenConverter());
        }
    
        // JWT 签名秘钥
        private static final String SIGNING_KEY = "wj-key";
    
        @Bean
        public JwtAccessTokenConverter jwtAccessTokenConverter(){
            JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
            ClassPathResource classPathResource = new ClassPathResource("public.txt");
            String publicKey = null;
            try {
                publicKey=IOUtils.toString(classPathResource.getInputStream(),"UTF-8");
                log.info("publicKey:{}" , publicKey);
            } catch (IOException e) {
                e.printStackTrace();
            }
            jwtAccessTokenConverter.setVerifierKey(publicKey);
            return jwtAccessTokenConverter;
        }
    }
    

    测试

    先请求access_token令牌:发现比对称加密的字符数目多了不少

    image-20210309153325913

    发送请求,仍然可以请求成功

    image-20210309153418251

  • 相关阅读:
    JavaScript的封装
    JavaScript接口
    JavaScript继承与聚合
    JavaScript原型模式(prototype)
    Maven学习总结(三)——使用Maven构建项目
    Maven学习总结(二)——Maven项目构建过程练习
    MyEclipse使用总结——MyEclipse10安装SVN插件
    Maven学习总结(一)——Maven入门
    使用Maven搭建Struts2框架的开发环境
    使用Maven编译项目遇到——“maven编码gbk的不可映射字符”解决办法
  • 原文地址:https://www.cnblogs.com/wwjj4811/p/14505886.html
Copyright © 2020-2023  润新知