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()")
        ;
    }
}
posted @ 2021-02-03 16:32  龙益阳  阅读(622)  评论(0编辑  收藏  举报