当前位置: 代码迷 >> 综合 >> 分布式系统Spring Cloud+Spring Security+OAuth2.0+JWT+Eureka+Spring Gateway实现授权和认证管理
  详细解决方案

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

热度:57   发布时间:2023-10-29 01:50:06.0

 介绍:

本项目主要介绍用户的授权认证管理在分布式系统的应用,使用的是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生成策略*/@Autowiredprivate TokenStore tokenStore;/*** 授权码服务类*/@Autowiredprivate AuthorizationCodeServices authorizationCodeServices;/*** 身份信息管理类*/@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate JwtAccessTokenConverter jwtAccessTokenConverter;/*** token信息的额外信息处理*/@Autowiredprivate CustomTokenEnhancer customTokenEnhancer;@Autowiredprivate DataSource dataSource;/*** clientDetail 信息里的client_secret字段加解密器* client_secret密码加密器** @return*/@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();
//        return NoOpPasswordEncoder.getInstance();}/*** 配置客户端信息* 配置从哪里获取ClientDetails信息* 在client_credentials授权方式下,只要这个ClientDetails信息。** @param clients* @throws Exception*/@Overridepublic 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*/@Beanpublic AuthorizationServerTokenServices tokenServices() {DefaultTokenServices services = new DefaultTokenServices();services.setSupportRefreshToken(true); //支持refreshtokenservices.setTokenStore(tokenStore);//token的保存方式services.setTokenEnhancer(tokenEnhancer());//token里加点信息return services;}@Beanpublic AuthorizationCodeServices authorizationCodeServices(DataSource dataSource) {return new JdbcAuthorizationCodeServices(dataSource);}/*** 配置认证服务端点** @param endpoints* @throws Exception*/@Overridepublic 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令牌的话**/@Overridepublic 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 {@Autowiredprivate UserInfoDao userInfoDao;@Overridepublic 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";@Autowiredprivate UserDetailsService userDetailsService;@Beanpublic TokenStore tokenStore() {return new JwtTokenStore(accessTokenConverter());}@Beanpublic 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 {@Autowiredprivate MyUserDetailsServiceImpl authUserDetailsService;/*** 获取表单提交的信息* @param authentication* @return* @throws AuthenticationException*/@Overridepublic 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*/@Overridepublic boolean supports(Class<?> authentication) {return true;}
}
  相关解决方案