分布式系统Spring Cloud+Spring Security+OAuth2.0+JWT+Eureka+Spring Gateway实现授权和认证管理

 介绍:

本项目主要介绍用户的授权认证管理在分布式系统的应用,使用的是spring-cloud-starter-oauth2

 主要实现以下功能:

1:通过用户名和密码进行鉴权,获取接口调用token

2:通过token进行资源服务器的访问

软件架构:

 

项目架构:

1:daisyday-manage-auth:oauth2.0鉴权认证服务

2:daisyday-manage-eureka:eureka注册服务,用于springcloud微服务注册

3:daisyday-manage-gateway:网关服务,使用的是Spring Cloud Gateway做路由、token认证、负载均衡

4:daisyday-service-order:资源服务,本文以订单系统为例

说明:oauth.sql 是鉴权和认证所需要的的表和数据

代码:

https://gitee.com/duyunming/daisyday-core-project.git

核心代码说明:

一、AuthorizationServer类继承AuthorizationServerConfigurerAdapter(认证服务配置适配器) 我们需要重写三个方法(不写就是用默认的, 根据需求,一般需要重写)


  【1】:@Override 
  public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
     访问安全配置。
  }
 【2】: @Override 
  public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    决定clientDeatils信息的处理服务
 }
 【3】: @Override 
   public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
     访问端点配置。tokenStroe、tokenEnhancer服务
 }
package com.daisyday.manage.auth.config;

import com.daisyday.manage.auth.enhancer.CustomTokenEnhancer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
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.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.sql.DataSource;
import java.util.Arrays;

@Configuration
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
    /**
     * token生成策略
     */
    @Autowired
    private TokenStore tokenStore;
    /**
     * 授权码服务类
     */
    @Autowired
    private AuthorizationCodeServices authorizationCodeServices;
    /**
     * 身份信息管理类
     */
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    /**
     * token信息的额外信息处理
     */
    @Autowired
    private CustomTokenEnhancer customTokenEnhancer;

    @Autowired
    private DataSource dataSource;

    /**
     * clientDetail 信息里的client_secret字段加解密器
     * client_secret密码加密器
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
//        return NoOpPasswordEncoder.getInstance();
    }

    /**
     * 配置客户端信息
     * 配置从哪里获取ClientDetails信息
     * 在client_credentials授权方式下,只要这个ClientDetails信息。
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        /**
         * 一般生产环境,将客户端信息存储在内存中
         */
        clients.jdbc(dataSource);
        /**
         * 测试用,将客户端信息存储在内存中
         */
        /*clients.inMemory()
                .withClient("c1")   // client_id
                .secret("secret")       // client_secret
                .authorizedGrantTypes("authorization_code")     // 该client允许的授权类型
                .scopes("app")     // 允许的授权范围
                .autoApprove(true); //登录后绕过批准询问(/oauth/confirm_access)*/
    }

    /**
     * token增强类
     *
     * @return
     */
    public TokenEnhancerChain tokenEnhancer() {
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(customTokenEnhancer, jwtAccessTokenConverter));
        return tokenEnhancerChain;
    }

    /**
     * token 令牌服务
     *
     * @return
     */
    @Bean
    public AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices services = new DefaultTokenServices();
        services.setSupportRefreshToken(true); //支持refreshtoken
        services.setTokenStore(tokenStore);//token的保存方式
        services.setTokenEnhancer(tokenEnhancer());//token里加点信息
        return services;
    }

    @Bean
    public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource) {
        return new JdbcAuthorizationCodeServices(dataSource);
    }

    /**
     * 配置认证服务端点
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

        endpoints
                .authenticationManager(authenticationManager)
                .authorizationCodeServices(authorizationCodeServices)
                .tokenServices(tokenServices())
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);
        /**
         * 替换原有的默认授权地址为新的授权地址
         */
//        endpoints.pathMapping("/oauth/token", "/oauth/login");
    }

    /**
     * 配置:安全检查流程,用来配置令牌端点(Token Endpoint)的安全与权限访问
     * 默认过滤器:BasicAuthenticationFilter
     * 1、oauth_client_details表中clientSecret字段加密【ClientDetails属性secret】
     * 2、CheckEndpoint类的接口 oauth/check_token 无需经过过滤器过滤,默认值:denyAll()
     * 对以下的几个端点进行权限配置:
     * /oauth/authorize:授权端点
     * /oauth/token:令牌端点
     * /oauth/confirm_access:用户确认授权提交端点
     * /oauth/error:授权服务错误信息端点
     * /oauth/check_token:用于资源服务访问的令牌解析端点
     * /oauth/token_key:提供公有密匙的端点,如果使用JWT令牌的话
     **/
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("permitAll()")
                .allowFormAuthenticationForClients();
    }
}

二、MyUserDetailsServiceImpl实现UserDetailsService接口。希望用户的信息来自数据库,而不是写死的,所以我们就需要实现UserDetailsService接口

package com.daisyday.manage.auth.service;


import com.daisyday.manage.auth.dao.db1.UserInfoDao;
import com.daisyday.manage.auth.entry.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

/**
 * 希望用户的信息来自数据库,而不是写死的,所以我们就需要实现UserDetailsService接口
 */
@Service
public class MyUserDetailsServiceImpl implements UserDetailsService {


    @Autowired
    private UserInfoDao userInfoDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserInfo userInfo=userInfoDao.selectByUserName(username);
        if(userInfo==null){
            throw new UsernameNotFoundException("username or password error");
        }
        String[] permissionArray = new String[1];
        permissionArray[0]="admin";
        // 将用户名称/用户密码以及用户拥有的权限放入UserDetails中
        UserDetails userDetails = User.withUsername(username)
                                      .password(userInfo.getPassword())
                                      .authorities(permissionArray)
                                      .build();
        return userDetails;
    }


}

三、TokenStoreConfig自定义生成access_token的类型和access_token的存储位置

可以看到access_token有五种生成和存储实现,本项目使用的是JwtTokenStore

package com.daisyday.manage.auth.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter;
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;

/**
 * token的保存方式
 *
 * @author DaisyDay
 */
@Configuration
public class TokenStoreConfig {

    private static final String SIGNING_KEY = "auth123456";

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
        DefaultUserAuthenticationConverter userTokenConverter = new DefaultUserAuthenticationConverter();

        userTokenConverter.setUserDetailsService(userDetailsService);
        tokenConverter.setUserTokenConverter(userTokenConverter);
        jwtAccessTokenConverter.setAccessTokenConverter(tokenConverter);
        /**
         * 对称秘钥,资源服务器使用该秘钥来验证
         */
        jwtAccessTokenConverter.setSigningKey(SIGNING_KEY);
        return jwtAccessTokenConverter;
    }
}

四、MyAuthenticationProvider实现AuthenticationProvider接口来进行自定义认证

package com.daisyday.manage.auth.config;

import com.daisyday.manage.auth.service.MyUserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Collection;

/**
 * AuthenticationProvider方式来进行自定义认证
 */
@Component
public class MyAuthenticationProvider implements  AuthenticationProvider {
    @Autowired
    private MyUserDetailsServiceImpl authUserDetailsService;

    /**
     * 获取表单提交的信息
     * @param authentication
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        Object credentials = authentication.getCredentials();
        String name = authentication.getName();
        Object principal = authentication.getPrincipal();
        //获取用户信息
        UserDetails user = authUserDetailsService.loadUserByUsername(name);
        //获取用户权限信息
        Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
        return new UsernamePasswordAuthenticationToken(user, principal, authorities);
    }

    /**
     * @Description 如果该AuthenticationProvider支持传入的Authentication对象,则返回true
     * @param authentication
     * @return
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return true;
    }
}
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

慕菲烟云

坚持原创,希望大家多多交流

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值