• OAuth2.0-2jwt令牌


    JWT令牌

    解决了之前普通令牌每次都要远程校验令牌带来得网络消耗;(有网友说可以将令牌验证从认证服务器上放到各个资源服务器上,不知是否可行?)

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

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

      1.1 令牌结构
        通过学习JWT令牌结构为自定义jwt令牌打好基础。
        JWT令牌由三部分组成,每部分中间使用点(.)分隔,比如:xxxxx.yyyyy.zzzzz

      Header
      头部包括令牌的类型(即JWT)及使用的哈希算法(如HMAC SHA256或RSA) 一个例子如下:
      下边是Header部分的内容:

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

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

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

    {
        "sub": "1234567890",
        "name": "456",
        "admin": true
    }

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

    HMACSHA256(
        base64UrlEncode(header) + "." +base64UrlEncode(payload),secret
    )

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

     有网友说:实际投产中,使用redis好一点,如果客户端过多,jwt就会很臃肿?

     

     1.授权服务AuthorizationServerConfig

    @Configuration
    //开启oauth2,auth server模式
    @EnableAuthorizationServer
    public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    
        @Autowired
        private PasswordEncoder passwordEncoder;
    
        //配置客户端
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                    //client的id和密码
                    .withClient("client1")
                    .secret(passwordEncoder.encode("123123"))
                    //给client一个id,这个在client的配置里要用的
                    .resourceIds("resource1")
                    //允许的申请token的方式,测试用例在test项目里都有.
                    //authorization_code授权码模式,这个是标准模式
                    //implicit简单模式,这个主要是给无后台的纯前端项目用的
                    //password密码模式,直接拿用户的账号密码授权,不安全
                    //client_credentials客户端模式,用clientid和密码授权,和用户无关的授权方式
                    //refresh_token使用有效的refresh_token去重新生成一个token,之前的会失效
                    .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
                    //授权的范围,每个resource会设置自己的范围.
                    .scopes("scope1", "scope2")
                    //这个是设置要不要弹出确认授权页面的.
                    .autoApprove(false)
                    //这个相当于是client的域名,重定向给code的时候会跳转这个域名
                    .redirectUris("http://www.baidu.com");
        }
        @Autowired
        private ClientDetailsService clientDetailsService;
        @Autowired
        private TokenStore tokenStore;
        @Autowired
        private JwtAccessTokenConverter tokenConverter;
        //配置token管理服务
        @Bean
        public AuthorizationServerTokenServices tokenServices() {
            DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
            defaultTokenServices.setClientDetailsService(clientDetailsService);
            defaultTokenServices.setSupportRefreshToken(true);
            //配置token的存储方法
            defaultTokenServices.setTokenStore(tokenStore);
            defaultTokenServices.setAccessTokenValiditySeconds(300);
            defaultTokenServices.setRefreshTokenValiditySeconds(1500);
            //配置token增强,把一般token转换为jwt token
            TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
            tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenConverter));
            defaultTokenServices.setTokenEnhancer(tokenEnhancerChain);
            return defaultTokenServices;
        }
        //密码模式才需要配置,认证管理器
        @Autowired
        private AuthenticationManager authenticationManager;
    
        //把上面的各个组件组合在一起
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.authenticationManager(authenticationManager)//认证管理器
                    .authorizationCodeServices(new InMemoryAuthorizationCodeServices())//授权码管理
                    .tokenServices(tokenServices())//token管理
                    .allowedTokenEndpointRequestMethods(HttpMethod.POST);
        }
        //配置哪些接口可以被访问
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            security.tokenKeyAccess("permitAll()")///oauth/token_key公开
                    .checkTokenAccess("permitAll()")///oauth/check_token公开
                    .allowFormAuthenticationForClients();//允许表单认证
        }
    }
    TokenConfig
    @Configuration
    public class TokenConfig {
    
        //配置如何把普通token转换成jwt token
        @Bean
        public JwtAccessTokenConverter tokenConverter() {
            JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    
            //使用对称秘钥加密token,resource那边会用这个秘钥校验token
            converter.setSigningKey("uaa123");
            return converter;
        }
    
        //配置token的存储方法 
        @Bean
        public TokenStore tokenStore() {
            //把用户信息都存储在token当中,相当于存储在客户端,性能好很多
            return new JwtTokenStore(tokenConverter());
        }
    }
    WebSecurityConfig
    @Configuration
    @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        //密码模式才需要配置,认证管理器
        @Bean
        @Override
        protected AuthenticationManager authenticationManager() throws Exception {
            return super.authenticationManager();
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()
                    .authorizeRequests()
                    .anyRequest().permitAll()
    
                    .and()
                    .formLogin()
    
                    .and()
                    .logout();
        }
    
        @Override
        @Bean
        public UserDetailsService userDetailsService() {
            /**
             * 基于内存创建用户
             */
            InMemoryUserDetailsManager manager=new InMemoryUserDetailsManager();
    
            manager.createUser(User.withUsername("zhangsan").password(passwordEncoder().encode("123")).authorities("admin").build());
            manager.createUser(User.withUsername("lisi").password(passwordEncoder().encode("123")).authorities("user").build());
            return manager;
        }
    }

    2.资源服务ResourceServerConfig

    @Configuration
    //开启oauth2,reousrce server模式
    @EnableResourceServer
    public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
        @Autowired
        private TokenStore tokenStore;
    
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources
                    //设置我这个resource的id, 这个在auth中配置, 这里必须照抄
                    .resourceId("resource1")
                    .tokenStore(tokenStore)//原来是 .tokenServices(tokenServices())去调用远程验证,现在只需要自己验证自己即可
                    //这个貌似是配置要不要把token信息记录在session中
                    .stateless(true);
        }
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()
                    .authorizeRequests()
    
                    //本项目所需要的授权范围,这个scope是写在auth服务的配置里的
                    .antMatchers("/**").access("#oauth2.hasScope('scope1')")
    
                    .and()
    
                    //这个貌似是配置要不要把token信息记录在session中
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }
    }
    TokenConfig
    @Configuration
    public class TokenConfig {
    
        //配置如何把普通token转换成jwt token
        @Bean
        public JwtAccessTokenConverter tokenConverter() {
            JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    
            //使用对称秘钥加密token,resource那边会用这个秘钥校验token
            converter.setSigningKey("uaa123");//密钥必须跟授权服务那边一样!
            return converter;
        }
    
        //配置token的存储方法
        @Bean
        public TokenStore tokenStore() {
            //把用户信息都存储在token当中,相当于存储在客户端,性能好很多
            return new JwtTokenStore(tokenConverter());
        }
    }
    WebSecurityConfig
    @Configuration
    @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()
                    .authorizeRequests()
                    .anyRequest().permitAll();
        }
    }

    验证方式:参考上一章

    //授权码模式
    //浏览器访问
    http://127.0.0.1:9011/oauth/authorize?client_id=client1&response_type=code&scope=scope1&redirect_uri=http://www.baidu.com

     带令牌访问资源:

     无需远程验证,直接令牌自己根据对称加解密 即可获取用户令牌内得权限信息

  • 相关阅读:
    Xshell相关优化
    Inotify+rsync远程实时同步
    MySQL主从复制故障解决
    Docker部署centos7容器
    Salt-ssh批量部署minion
    MySQL数据库二
    防火墙2
    MySQl数据库
    防火墙
    http原理2
  • 原文地址:https://www.cnblogs.com/jiawen010/p/12978313.html
Copyright © 2020-2023  润新知