Spring Cloud微服务安全实战_6-2_jwt认证之认证服务改造

Posted 我俩绝配

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Cloud微服务安全实战_6-2_jwt认证之认证服务改造相关的知识,希望对你有一定的参考价值。

一 、认证服务器上发Token的改造:uuid字符串改造为JWT

之前生成的token都是一个无意义的字符串uuid,也存在着上一篇 https://www.cnblogs.com/lihaoyang/p/12203586.html 所说的几个问题。本篇就要把token改造成JWT。

在认证服务器配置类AuthorizationServerConfigurerAdapter 的实现类  OAuth2AuthServerConfig 里,做相应的改造:

1,在TokenStore里,换掉之前的JdbcTokenStore,换成JwtTokenStore

  需要new一个JwtAccessTokenConverter 转换器,传给 JwtTokenStore,这个Converter作用是设置 对 jwt 进行签名的key。

2,在配置方法 configure(AuthorizationServerEndpointsConfigurer endpoints) 里,设置  endpoints.tokenEnhancer(jwtTokenEnhancer()) 

3,在配置方法  configure(AuthorizationServerSecurityConfigurer security) 里,设置 security.tokenKeyAccess("isAuthenticated()") ,会自动对外暴漏一个获取JWT签名的key的服务。而且是需要经过身份 认证的才能请求该服务。

OAuth2AuthServerConfig 源码:
package com.nb.security.server.auth;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;

import javax.sql.DataSource;

/**
 * Created by: 李浩洋 on 2019-10-29
 *
 * 认证服务器
 **/
@EnableRedisHttpSession //启用spring session redis
@Configuration  //这是一个配置类
@EnableAuthorizationServer //当前应用是一个认证服务器
public class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter {//AuthorizationServerConfigurerAdapter:认证服务器适配器

    //Spring 对密码加密的封装,自己配置下
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Qualifier("dataSource")
    @Autowired
    private DataSource dataSource;

    @Autowired
    private UserDetailsService userDetailsService;

    //tokenStore是进行存取token的接口,默认内存的实现还有redis,jdbc,jwt的实现(idea ctrl+H可看类关系)

    @Bean
    public TokenStore tokenStore(){
        //return new JdbcTokenStore(dataSource);//这里配置用jdbc进行存取token
        return new JwtTokenStore(jwtTokenEnhancer());//jwt存取token
    }


    private JwtAccessTokenConverter jwtTokenEnhancer() {
        /**
         * 对jwt进行签名的key,jwt是明文,签名防篡改。
         * 接收token的人需要用同样的key验签名,需要把这个key通过服务暴漏出去,使用服务的人才能拿到key
         */
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("lihaoyang");
        return converter;
    }

    /**
     * 1,配置客户端应用的信息,让认证服务器知道有哪些客户端应用来申请令牌。
     *
     * ClientDetailsServiceConfigurer:客户端的详情服务的配置
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        /////////2--从数据库里读取客户端应用配置信息,需要一个数据源,
        // spring会自动去  oauth_client_details 表里读取客户端信息
        clients.jdbc(dataSource);
    }


   /* @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        ///////////////1----配置在了内存里 //////////////////////
        clients.inMemory()//配置在内存里,后面修改为数据库里
                //~============== 注册【客户端应用】,使客户端应用能够访问认证服务器 ===========
                .withClient("orderApp")
                .secret(passwordEncoder.encode("123456")) //123456 加密后 $2a$10$smHIzJWYZGDUR7zvtDWDZuCw7awklq23MBll/vETtEHHd37gdl.9K
                .scopes("read","write") //orderApp有哪些权限
                .accessTokenValiditySeconds(3600) //token的有效期
                .resourceIds("order-server") //资源服务器的id。发给orderApp的token,能访问哪些资源服务器,可以多个
                .authorizedGrantTypes("password")//授权方式,再给orderApp做授权的时候可以用哪种授权方式授权
                //~=============客户端应用配置结束 =====================
                .and()
                //~============== 注册【资源服务器-订单服务】(因为订单服务需要来认证服务器验令牌),使订单服务也能够访问认证服务器 ===========
                .withClient("orderService")
                .secret(passwordEncoder.encode("123456")) //123456 加密后 $2a$10$IW8NdL0L.0MPech5NmhYLen3vLj7tVeGrXi6sIV.u.WlBp1VKBcCm
                .scopes("read") //orderServer有哪些权限
                .accessTokenValiditySeconds(3600) //token的有效期
                .resourceIds("order-server") //资源服务器的id。
                .authorizedGrantTypes("password");//授权方式,
    }*/

    public static void main(String[] args) {
        //手动加密123456
        System.err.println(new BCryptPasswordEncoder().encode("123456"));
    }

    /**
     *,2,配置用户信息
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //传给他一个authenticationManager用来校验传过来的用户信息是不是合法的,注进来一个,自己实现
        endpoints
                //这里指定userDetailsService是专门给refresh_token用的,其他的四种授权模式不需要这个
                .userDetailsService(userDetailsService)

                .tokenStore(tokenStore()) //告诉服务器要用自定义的tokenStore里去存取token
                .tokenEnhancer(jwtTokenEnhancer()) //加入jwt需要用到
                .authenticationManager(authenticationManager);
    }


    /**
     * 3,配置资源服务器过来验token 的规则
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {

        security
                /**
                 * 暴漏验token的服务,过来验令牌有效性的请求,不是谁都能验的,只有经过身份认证的请求才能调。
                 * 所谓身份认证就是,必须携带clientId,clientSecret,否则随便一请求过来验token是不验的
                 */
                .checkTokenAccess("isAuthenticated()")
                /**
                 * 暴漏获取jwt签名key的服务,只有经过身份认证的请求才能调(同上)
                 * 上边 tokenStore里的signKey
                 */
                .tokenKeyAccess("isAuthenticated()");
    }
}

 启动认证服务器

clinet_details表数据

 

 

 用PostMan做实验,直接请求认证服务器,申请令牌接口 localhost:9090/oauth/token

 

 

 

 

 

 返回数据

{
    "access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsib3JkZXItc2VydmVyIl0sInVzZXJfbmFtZSI6ImxoeSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJleHAiOjE1ODQwNzY1MzMsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwianRpIjoiZjI3MmNjYTktNzc3OS00MGU5LWEzZWYtNGZlMTViNjllNWJlIiwiY2xpZW50X2lkIjoiYWRtaW4ifQ.ANQ1BtRnQPJOMvnN5roYd8i1nmGNY1pnMjUVFIr2KPc",
    "token_type":"bearer",
    "refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsib3JkZXItc2VydmVyIl0sInVzZXJfbmFtZSI6ImxoeSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJhdGkiOiJmMjcyY2NhOS03Nzc5LTQwZTktYTNlZi00ZmUxNWI2OWU1YmUiLCJleHAiOjE1ODQwNzY1NDMsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwianRpIjoiZDJlYWU1MzgtYjMxYy00NWUyLWJiNTktYzhlNWIxODgwYTAxIiwiY2xpZW50X2lkIjoiYWRtaW4ifQ.n-ySt7qqhCvRLLgGqw1GqJe6n10-4GB5J3tCa38x1bQ",
    "expires_in":19,
    "scope":"read write",
    "jti":"f272cca9-7779-40e9-a3ef-4fe15b69e5be"
}

https://jwt.io/ 网址 解析一下返回得access_token 信息:

 至此,已经把token由无意义的uuid字符串,改造成了自包含用户信息JWT。

代码  :https://github.com/lhy1234/springcloud-security/tree/chapt-6-1-jwt01

 

 

欢迎关注个人公众号一起交流学习:

 

 

以上是关于Spring Cloud微服务安全实战_6-2_jwt认证之认证服务改造的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud微服务安全实战_4-1_微服务网关安全_概述&微服务安全面临的挑战

Spring Cloud微服务安全实战_6-1_微服务之间的通讯安全之概述

Spring Cloud微服务安全实战_4-10_用spring-cloud-zuul-ratelimit做限流

Spring Cloud微服务安全实战_3-8_API安全之登录

Spring Cloud微服务安全实战_3-4_API安全机制之认证

Spring Cloud微服务安全实战_5-5_refresh token失效处理