欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Security + Oauth2实现认证服务器

程序员文章站 2022-06-13 20:13:59
...

Spring Security + Oauth2+JWT实现认证服务器

一、依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
    <version>2.2.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 切面编程 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 工具包 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.9</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.4</version>
</dependency>
<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.4</version>
</dependency>
</dependencies>

二、安全配置

/**
 * @Author: fxx
 * @Date: 2020/5/16 8:52
 * web安全配置
 */
@Configuration
@EnableWebSecurity
public class DcSecurityConfig extends WebSecurityConfigurerAdapter {

    //这个是password模式需要用到的一个bean
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    //加密
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
            	//下面url放行
                .antMatchers("/oauth/**", "/sign", "/test").permitAll()
            	//任何请求都要认证
                .anyRequest().authenticated()
                .and()
            	//关闭csrf防护
                .csrf().disable();
    }
}

三、认证服务器配置

/**
 * @Author: fxx
 * @Date: 2020/5/15 16:36
 * 认证服务器配置
 */
@Configuration
@EnableAuthorizationServer
public class DcAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    @Qualifier("userServiceImpl")
    private UserDetailsService userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    //token存储
    @Autowired
    @Qualifier("jwtTokenStore")
    private TokenStore tokenStore;

    //jwt令牌部分
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    private TokenEnhancer jwtTokenEnhancer;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .tokenStore(tokenStore)
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
        //写一个token增强器到token增强里
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> enhancers = new ArrayList<>();
        enhancers.add(jwtTokenEnhancer);
        enhancers.add(jwtAccessTokenConverter);
        tokenEnhancerChain.setTokenEnhancers(enhancers);
        endpoints
                .tokenEnhancer(tokenEnhancerChain)
                .accessTokenConverter(jwtAccessTokenConverter);
    }

    //配置允许哪些应用可以访问服务器
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("client")//clientId
                .secret(passwordEncoder.encode("secret"))//clientSecret
                .accessTokenValiditySeconds(7200)//token过期时间
                .authorizedGrantTypes("refresh_token", "password")//允许的认证模式:刷新token和密码模式
                .scopes("all")//访问的域
                .redirectUris("http://localhost:8083");//回调地址
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //允许/oauth/check_token被访问,默认不允许访问
        security.checkTokenAccess("permitAll()");
    }

}

四、token存储

/**
 * @Author: fxx
 * @Date: 2020/5/20 10:27
 * token存储
 */
@Configuration
public class TokenStoreConfig {

    @Configuration
    public static class JwtTokenConfig{

        /**
         * token存储,存储在jwt里
         * @return
         */
        @Bean
        public TokenStore jwtTokenStore(){
            return new JwtTokenStore(jwtAccessTokenConverter());
        }

        /**
         * token生成
         */
        @Bean
        public JwtAccessTokenConverter jwtAccessTokenConverter(){
            JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
            //设置密签
            accessTokenConverter.setSigningKey("secret");
            return accessTokenConverter;
        }

        /**
         * 注册jwt增强器
         */
        @Bean
        public TokenEnhancer jwtTokenEnhancer(){
            return new HzudcJwtTokenEnhancer();
        }
    }
}

五、jwt附加信息

/**
 * @Author: fxx
 * @Date: 2020/5/20 10:33
 */
public class DcJwtTokenEnhancer implements TokenEnhancer {

    @Autowired
    private UserService userService;

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
        Map<String, Object> info = new HashMap<>();
        info.put("phone", "12345678912");
        //设置附加信息
        ((DefaultOAuth2AccessToken)oAuth2AccessToken).setAdditionalInformation(info);
        return oAuth2AccessToken;
    }
}

六、自定义token返回格式

/**
 * @Author: fxx
 * @Date: 2020/5/25 14:15
 * 通过切面拿到/oauth/token的返回值,做增强处理
 */
@Component
@Aspect
public class AuthTokenAspect {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Around("execution(* org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(..))")
    public Object handleTokenControllerMethod(ProceedingJoinPoint pjp) throws Throwable{
        logger.info("/oauth/token执行前");
        //调用目标方法
        Object proceed = pjp.proceed();
        logger.info("/oauth/token执行后");
        //自定义的返回类
        Result result = new Result();
        if (proceed != null){
            //ResponseEntity<OAuth2AccessToken>是方法的默认返回类型
            ResponseEntity<OAuth2AccessToken> responseEntity = (ResponseEntity<OAuth2AccessToken>)proceed;
            //用debug看可以看到body里是jwt信息
            OAuth2AccessToken body = responseEntity.getBody();
            //成功返回
            if (responseEntity.getStatusCode().is2xxSuccessful()) {
                result.setCode(200);
                result.setMessage("登录成功");
                result.add("token", body);
            } else {
                logger.error("error:", responseEntity.getStatusCode().toString());
                result.setCode(100);
                result.setMessage("用户名密码错误!");
            }
        }
        return ResponseEntity
                .status(200)
                .body(result);
    }
}

七、结果

Spring Security + Oauth2实现认证服务器

八、参考

自定义token返回格式部分参考自:

自定义spring security oauth /auth/token的返回内容格式