zoukankan      html  css  js  c++  java
  • spring cloud oauth2(一) 授权服务搭建

    依赖

    注:使用的springboot版本 2.2.4.RELEASE ;spring cloud 版本 Hoxton.SR1

    加入oauth2和web相关的依赖

          <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
      <!--   spring-cloud-starter-oauth2 已经包含了security等相关依赖 只需要导入即可   -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-oauth2</artifactId>
            </dependency>
    

    集成步骤

    • 继承WebSecurityConfigurerAdapter 配置security相关配置
    • 继承AuthorizationServerConfigurerAdapter 配置授权服务
    • 实现**UserDetailsService** ,查询客户信息

    WebSecurityConfig配置

    package com.Lonni.oauth.config;
    
    import cn.hutool.crypto.digest.DigestUtil;
    import com.Lonni.oauth.exception.filter.CustomOncePerRequestFilter;
    import com.Lonni.oauth.provider.MobileCodeAuthenticationProvider;
    import com.Lonni.oauth.service.UserDetailsServiceImpl;
    import org.apache.tomcat.util.digester.Digester;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.core.annotation.Order;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.http.HttpMethod;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.AuthorityUtils;
    import org.springframework.security.core.userdetails.User;
    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.provisioning.InMemoryUserDetailsManager;
    import org.springframework.security.web.header.HeaderWriterFilter;
    
    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.Set;
    
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        /**
         * 查询客户端是需要用这个加解密 而不是原始的
         * @return
         */
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        /**
         * 配置redis
         */
        @Autowired
        private RedisTemplate redisTemplate;
    
        /**
         * 查询用户信息
         */
        @Autowired
        private UserDetailsServiceImpl userDetailsService;
    
        /**
         * 配置密码模式需要的AuthenticationManager
         * @return
         * @throws Exception
         */
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            AuthenticationManager manager = super.authenticationManagerBean();
            return manager;
        }
        /**
         * 这里是对认证管理器的添加配置
         *
         * @param auth
         * @throws Exception
         */
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth
                   
                    //设置查询的userDetailsService
                    .userDetailsService(userDetailsService)
                    //设置密码加密器 设置为自定义工具类 而不是使用security自带的
                    .passwordEncoder(new PasswordEncoder() {
                        @Override
                        public String encode(CharSequence charSequence) {
                            String mdStr = DigestUtil.sha1Hex((String) charSequence);
                            return mdStr;
                        }
    
                        @Override
                        public boolean matches(CharSequence charSequence, String encodedPassword) {
                            return encodedPassword.equals(DigestUtil.sha1Hex((String) charSequence));
                        }
                    });
    
    
        }
    
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()
                    .authorizeRequests()
                    .anyRequest().permitAll()
                    .and()
                    .formLogin()
                    .and()
                    .logout();
    
          
        }
    
    }
    

    .passwordEncoder方法设置在查询用户时密码比对的加密器;可根据自己的需要替换

    配置 AuthorizationServer 服务

    • 配置jwt token配置类
    • 配置jwt token增强器
    • 配置server配置
    jwt token配置类
    package com.Lonni.core.aouth2.config;
    
    import com.Lonni.common.constants.AuthConstant;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.oauth2.provider.token.TokenStore;
    import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
    import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
    
    /**
     * @program: Lonni-plus
     * @description: jwttoken配置类
     * @author: lonni
     * @create: 2020-11-19 21:18
     */
    @Configuration
    public class JwtTokenConfig {
    
        /**
         * 1:配置 JWT 数据转换
         * 设置解密的key
         * @return
         */
        @Bean("jwtAccessTokenConverter")
        public JwtAccessTokenConverter jwtAccessTokenConverter(){
            JwtAccessTokenConverter converter=new JwtAccessTokenConverter();
            //设置为解密token的key 同样在加密的时候也要设置为这个key
            converter.setSigningKey(AuthConstant.AUTH_SECURITY_JWT_SIGN_KEY);
            return converter;
        }
        /**
         * 2:返回jwt存储器
         * @return
         */
        @Bean("jwtTokenStore")
        public TokenStore jwtTokenStore(){
            return  new JwtTokenStore(jwtAccessTokenConverter());
        }
    }
    
    配置jwt token增强器,在生成token时增加额外信息
    package com.Lonni.oauth.config;
    
    import com.Lonni.common.constants.AuthConstant;
    import com.Lonni.oauth.model.CustomUserDetails;
    import com.Lonni.oauth.utils.PrincipalUtil;
    import com.alibaba.fastjson.JSON;
    import com.google.common.collect.Maps;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken;
    import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
    import org.springframework.security.oauth2.common.OAuth2AccessToken;
    import org.springframework.security.oauth2.provider.OAuth2Authentication;
    import org.springframework.security.oauth2.provider.token.TokenEnhancer;
    
    import java.security.Principal;
    import java.util.Calendar;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 增强jwt使用
     */
    public class JWTokenEnhancer implements TokenEnhancer {
        @Override
        public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
            //password,mobile,captcha
            String grantType = oAuth2Authentication.getOAuth2Request().getGrantType();
            //生成jwt时不会生成所有的信息 需要自己存入
            if ("password".equals(grantType) || "mobile".equals(grantType) || "captcha".equals(grantType)) {
                Object principal = oAuth2Authentication.getPrincipal();
                //登录成功后就是cus
                CustomUserDetails userDetails = (CustomUserDetails) principal;
                Map<String, Object> info = new HashMap<>();
                //在这里扩展信息
                info.put("userId", userDetails.getUserId());
                info.put("userName", userDetails.getUsername());
                info.put("nickName", userDetails.getNickName());
                info.put("phone", userDetails.getPhone());
                info.put("openId", userDetails.getOpenId());
                info.put("unionId", userDetails.getUnionId());
                info.put("avatar", userDetails.getAvatar());
                DefaultOAuth2AccessToken auth2AccessToken = ((DefaultOAuth2AccessToken) oAuth2AccessToken);
                auth2AccessToken.setAdditionalInformation(info);
                //如果是client_id=app 过时时间为5天
                String clientId = oAuth2Authentication.getOAuth2Request().getClientId();
                if (AuthConstant.AUTH_APPLICATION_APP_ID.equals(clientId)) {
                    Calendar calen = Calendar.getInstance();
                    calen.add(Calendar.DAY_OF_YEAR, 5);
                    auth2AccessToken.setExpiration(calen.getTime());
                }
            }
            return oAuth2AccessToken;
        }
    }
    

    注意 : oAuth2Authentication.getPrincipal()如果是授权码模式或则客户端模式获取的时候就不是用户的信息,需要判断授权模式才转换

    配置server

    package com.Lonni.oauth.config;
    
    import com.Lonni.common.constants.AuthConstant;
    import com.Lonni.oauth.exception.filter.CustomAuthenticationEntryPoint;
    import com.Lonni.oauth.exception.filter.CustomClientCredentialsTokenEndpointFilter;
    import com.Lonni.oauth.granter.TokenGranterExt;
    import com.Lonni.oauth.service.UserDetailsServiceImpl;
    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.data.redis.core.RedisTemplate;
    import org.springframework.http.HttpMethod;
    import org.springframework.security.authentication.AuthenticationManager;
    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.ClientDetailsService;
    import org.springframework.security.oauth2.provider.TokenGranter;
    import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
    import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
    import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
    import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
    import org.springframework.security.oauth2.provider.token.*;
    import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
    
    import javax.sql.DataSource;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 配置认证服务文件
     */
    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
        /**
         * AuthenticationManager authenticationManager ;认证管理器;如果为password模式时必须配置
         * AuthorizationCodeServices authorizationCodeServices ;如果是授权码模式,必须设置此对象
         * ImplicitGrantService implicitGrantService;如果是ImplicitG模式 需要设置此对象
         * 访问资源服务器:
         * 在header头中加上
         *   Authorization: Bearer 你的token值
         */
    
    
        /**
         * 如果是密码模式 必须配置
         */
        @Autowired
        AuthenticationManager authenticationManager;
    
    
    
    
        /**
         * 密码加密器 新版本必须配置
         */
        @Autowired
        PasswordEncoder passwordEncoder;
    
    
        @Autowired
        private ClientDetailsService clientDetailsService;
    
        @Autowired
        private UserDetailsServiceImpl userDetailsService;
        @Autowired
        private RedisTemplate redisTemplate;
        /***
         * 将客户端信息存储到数据库
         *  1:配置数据库连接
         *  2:注入DataSource
         *  3:初始化ClientDetailsService 这里直接在config方法中返回jdbcclient
         *  4:修改configure(ClientDetailsServiceConfigurer clients)方法 ,使用数据库方式
         *  5:修改授权码实现类 AuthorizationCodeServices 为数据库方式
         *
         *
         */
        @Autowired
        private DataSource dataSource;
    
    
    
    
        /**
         * 1: 配置客户端的方法
         *
         * @param clients
         * @throws Exception
         */
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            //配置客户端存储到db
            JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
            //设置客户端的密码对比加密器
            clientDetailsService.setPasswordEncoder(passwordEncoder);
            //设置查询的sql
            clientDetailsService.setFindClientDetailsSql(AuthConstant.FIND_CLIENT_DETAILS_SQL);
            clientDetailsService.setSelectClientDetailsSql(AuthConstant.SELECT_CLIENT_DETAILS_SQL);
            clients.withClientDetails(clientDetailsService);
        }
    
    
    
        // 注入   TokenStore 使用 jwt方式 在jwtTokenConfig中配置了
        @Autowired
        @Qualifier("jwtTokenStore")
        private TokenStore tokenStore;
        // 注入   jwt token 转换器   在jwtTokenConfig中配置了
        @Autowired
        @Qualifier("jwtAccessTokenConverter")
        private JwtAccessTokenConverter jwtAccessTokenConverter;
    
    
        @Bean
        public TokenEnhancer jwtTokenEnhancer() {
            return new JWTokenEnhancer();
        }
    
        /**
         * 3 配置令牌服务 不管什么服务 都需要此配置
         *
         * @return
         */
        @Bean
        public AuthorizationServerTokenServices tokenServices() {
            DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
            //配置客户端信息服务
            defaultTokenServices.setClientDetailsService(clientDetailsService);
            //配置客户端令牌存储策略
            defaultTokenServices.setTokenStore(tokenStore);
    
    
            //是否产生刷新令牌  如果是jdbc方式  需要在数据库中设置,在这里设置无效
            defaultTokenServices.setSupportRefreshToken(true);
            //令牌的有效期 2小时
            defaultTokenServices.setAccessTokenValiditySeconds(7200);
            //刷新令牌的有效期 3天
            defaultTokenServices.setRefreshTokenValiditySeconds(259200);
    
    
            // 配置 token 转换器 jwt方式必须配置
            //配置token增加,把一般token转换为jwt token
            TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
            List<TokenEnhancer> enhancerList = new ArrayList<>();
            enhancerList.add(jwtTokenEnhancer());
            enhancerList.add(jwtAccessTokenConverter);
            tokenEnhancerChain.setTokenEnhancers(enhancerList);
            defaultTokenServices.setTokenEnhancer(tokenEnhancerChain);
            return defaultTokenServices;
        }
        /**
         * 4配置授权服务器的终结点和其他属性
         * 令牌访问端点
         * @param endpoints
         * @throws Exception
         */
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints
                    //设置密码模式认证器
                    .authenticationManager(authenticationManager)
                    //设置授权码模式认证器
                    .authorizationCodeServices(this.authorizationCodeServices())
                    //设置令牌策略
                    .tokenServices(tokenServices())
                    //设置查询用户的userDetilService
                    .userDetailsService(userDetailsService)
                    //允许post get提交认证
                    .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
            ;
        }
    
        /**
         * 配置授权码服务的实现类 数据库方式
         *
         * @return
         */
        @Bean
        public AuthorizationCodeServices authorizationCodeServices() {
            return new JdbcAuthorizationCodeServices(dataSource);
        }
    
        /**
         * 5令牌访问的安全策略
         * @param security
         * @throws Exception
         */
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            security
                    // 即clientId和clientSecret组合起来base加密后放在http header中传递
                   // .allowFormAuthenticationForClients()
                    //oauth/token_key是公开
                    .tokenKeyAccess("permitAll()")
                    //oauth/check_token公开
                    .checkTokenAccess("permitAll()")
            ;
        }
    }
    
  • 相关阅读:

    ATM三层架构思路
    一个项目的从无到有
    re模块
    logging模块
    物联网公共安全平台软件体系架构
    本科生怎样发表自己的论文
    Cloud Native 云化架构阅读笔记
    实验5 Spark SQL编程初级实践
    云计算环境下计算机软件系统架构分析
  • 原文地址:https://www.cnblogs.com/HiLzd/p/14367744.html
Copyright © 2011-2022 走看看