# daisyday-core-project **Repository Path**: duyunming/daisyday-core-project ## Basic Information - **Project Name**: daisyday-core-project - **Description**: daisyday-core-project - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 2 - **Created**: 2021-03-23 - **Last Updated**: 2023-07-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## daisyday-core-project ### 分布式系统Spring Cloud+Spring Security+OAuth2.0+JWT+Eureka+Spring Gateway实现授权和认证管理 #### 介绍 本项目主要介绍用户的授权认证管理在分布式系统的应用,使用的是spring-cloud-starter-oauth2 主要实现以下功能: 1:通过用户名和密码进行鉴权,获取接口调用token 2:通过token进行资源服务器的访问 #### 软件架构 ![架构图](./20210329162925642.png) #### 项目架构 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(认证服务配置适配器) 我们需要重写三个方法(不写就是用默认的, 根据需求,一般需要重写) ```java /** * 认证服务配置适配器 * 我们需要重写三个方法(不写就是用默认的, 根据需求,一般需要重写) * * @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { * 访问安全配置。 * } * @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { * 决定clientDeatils信息的处理服务 * } * @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { * 访问端点配置。tokenStroe、tokenEnhancer服务 * } */ @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接口 ```java /** * 希望用户的信息来自数据库,而不是写死的,所以我们就需要实现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的存储位置 ```java /** * 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接口来进行自定义认证 ```java /** * 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 authorities = user.getAuthorities(); return new UsernamePasswordAuthenticationToken(user, principal, authorities); } /** * @Description 如果该AuthenticationProvider支持传入的Authentication对象,则返回true * @param authentication * @return */ @Override public boolean supports(Class authentication) { return true; } } ```