feat:刷新token时选择userDetailService

This commit is contained in:
有来技术 2021-10-03 23:33:37 +08:00
parent fab7d40eb8
commit d86a9d549f
9 changed files with 124 additions and 14 deletions

View File

@ -33,7 +33,7 @@
</sql>
<resultMap id="UserAuthMap" type="com.youlai.admin.pojo.dto.UserAuthDTO">
<id property="userId" column="id" jdbcType="BIGINT"/>
<id property="userId" column="userId" jdbcType="BIGINT"/>
<result property="username" column="username" jdbcType="VARCHAR"/>
<result property="password" column="password" jdbcType="VARCHAR"/>
<result property="status" column="status" jdbcType="BOOLEAN"/>

View File

@ -6,6 +6,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@ -47,4 +48,15 @@ public class OAuthExceptionHandler {
return Result.failed(e.getMessage());
}
/**
* token 无效或已过期
*
* @param e
* @return
*/
@ExceptionHandler({InvalidTokenException.class})
public Result handleInvalidTokenExceptionException(InvalidTokenException e) {
return Result.failed(e.getMessage());
}
}

View File

@ -60,7 +60,7 @@ public class OAuthController {
* 方式一client_idclient_secret放在请求路径中(当前版本已废弃)
* 方式二放在请求头Request Headers中的Authorization字段且经过加密例如 Basic Y2xpZW50OnNlY3JldA== 明文等于 client:secret
*/
String clientId = JwtUtils.getOAuthClientId();
String clientId = JwtUtils.getOAuth2ClientId();
log.info("OAuth认证授权 客户端ID:{},请求参数:{}", clientId, JSONUtil.toJsonStr(parameters));
/**

View File

@ -5,11 +5,14 @@ import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import com.youlai.auth.security.core.clientdetails.ClientDetailsServiceImpl;
import com.youlai.auth.security.core.userdetails.member.MemberUserDetails;
import com.youlai.auth.security.core.userdetails.member.MemberUserDetailsServiceImpl;
import com.youlai.auth.security.core.userdetails.system.SysUserDetails;
import com.youlai.auth.security.core.userdetails.system.SysUserDetailsServiceImpl;
import com.youlai.auth.security.extension.wechat.WechatTokenGranter;
import com.youlai.auth.security.core.CustomUserDetailsByNameServiceWrapper;
import com.youlai.common.result.Result;
import com.youlai.common.result.ResultCode;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -17,6 +20,8 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
@ -24,11 +29,13 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.E
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
import java.security.KeyPair;
import java.util.*;
@ -38,12 +45,15 @@ import java.util.*;
*/
@Configuration
@EnableAuthorizationServer
@AllArgsConstructor
@RequiredArgsConstructor
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private AuthenticationManager authenticationManager;
private final AuthenticationManager authenticationManager;
private final ClientDetailsServiceImpl clientDetailsService;
private final SysUserDetailsServiceImpl sysUserDetailsService;
private final MemberUserDetailsServiceImpl memberUserDetailsService;
private ClientDetailsServiceImpl clientDetailsService;
private final List<UserDetailsService> userDetailsServices;
/**
* OAuth2客户端
@ -68,7 +78,8 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
// 添加自定义授权模式
List<TokenGranter> granterList = new ArrayList<>(Arrays.asList(endpoints.getTokenGranter()));
granterList.add(new WechatTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), authenticationManager));
granterList.add(new WechatTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(),
authenticationManager));
CompositeTokenGranter compositeTokenGranter = new CompositeTokenGranter(granterList);
endpoints
@ -76,12 +87,37 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
.accessTokenConverter(jwtAccessTokenConverter())
.tokenEnhancer(tokenEnhancerChain)
.tokenGranter(compositeTokenGranter)
// .userDetailsService(sysUserDetailsService)
// refresh token有两种使用方式重复使用(true)非重复使用(false)默认为true
// 1 重复使用access token过期刷新时 refresh token过期时间未改变仍以初次生成的时间为准
// 2 非重复使用access token过期刷新时 refresh token过期时间延续在refresh token有效期内刷新便永不失效达到无需再次登录的目的
.reuseRefreshTokens(true);
.reuseRefreshTokens(true)
.tokenServices(tokenServices(endpoints)); // 自定义的TokenService
}
public DefaultTokenServices tokenServices(AuthorizationServerEndpointsConfigurer endpoints) {
// Token增强
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
List<TokenEnhancer> tokenEnhancers = new ArrayList<>();
tokenEnhancers.add(tokenEnhancer());
tokenEnhancers.add(jwtAccessTokenConverter());
tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(endpoints.getTokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(clientDetailsService);
tokenServices.setTokenEnhancer(tokenEnhancerChain);
PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
provider.setPreAuthenticatedUserDetailsService(new CustomUserDetailsByNameServiceWrapper<>(userDetailsServices));
tokenServices.setAuthenticationManager(new ProviderManager(Arrays.asList(provider)));
return tokenServices;
}
/**
* 使用非对称加密算法对token签名
*/

View File

@ -55,8 +55,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(wechatAuthenticationProvider())
.authenticationProvider(daoAuthenticationProvider());
auth.authenticationProvider(wechatAuthenticationProvider());
auth.authenticationProvider(daoAuthenticationProvider());
}

View File

@ -0,0 +1,61 @@
package com.youlai.auth.security.core;
import com.youlai.auth.security.core.userdetails.member.MemberUserDetailsServiceImpl;
import com.youlai.auth.security.core.userdetails.system.SysUserDetailsServiceImpl;
import com.youlai.auth.security.extension.wechat.WechatAuthenticationToken;
import com.youlai.common.web.util.JwtUtils;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.util.Assert;
import java.util.List;
/**
* @author <a href="mailto:xianrui0365@163.com">xianrui</a>
* @date 2021/10/2
*/
@NoArgsConstructor
public class CustomUserDetailsByNameServiceWrapper<T extends Authentication> implements AuthenticationUserDetailsService<T>, InitializingBean {
private List<UserDetailsService> userDetailsServiceList;
public CustomUserDetailsByNameServiceWrapper(List<UserDetailsService> userDetailsServiceList) {
Assert.notNull(userDetailsServiceList, "userDetailsService cannot be null.");
this.userDetailsServiceList = userDetailsServiceList;
}
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.userDetailsServiceList, "UserDetailsService must be set");
}
@Override
public UserDetails loadUserDetails(T authentication) throws UsernameNotFoundException {
UserDetailsService userDetailsService = null;
String clientId = JwtUtils.getOAuth2ClientId();
for (UserDetailsService detailsService : userDetailsServiceList) {
if (clientId.equals("youlai-mall-weapp")) {
if (detailsService instanceof MemberUserDetailsServiceImpl) {
userDetailsService = detailsService;
continue;
}
}else{
if (detailsService instanceof SysUserDetailsServiceImpl) {
userDetailsService = detailsService;
continue;
}
}
}
return userDetailsService.loadUserByUsername(authentication.getName());
}
}

View File

@ -13,6 +13,9 @@ import org.springframework.security.oauth2.provider.NoSuchClientException;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.stereotype.Service;
/**
* OAuth2 客户端信息
*/
@Service
@RequiredArgsConstructor
public class ClientDetailsServiceImpl implements ClientDetailsService {

View File

@ -1,7 +1,5 @@
package com.youlai.auth.security.core.userdetails.member;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.youlai.common.result.Result;
import com.youlai.common.result.ResultCode;
import com.youlai.mall.ums.api.MemberFeignClient;

View File

@ -58,14 +58,14 @@ public class JwtUtils {
/**
* 获取登录认证的客户端ID
*
* 兼容两种方式获取Oauth2客户端信息client_idclient_secret
* 兼容两种方式获取OAuth2客户端信息client_idclient_secret
* 方式一client_idclient_secret放在请求路径中
* 方式二放在请求头Request Headers中的Authorization字段且经过加密例如 Basic Y2xpZW50OnNlY3JldA== 明文等于 client:secret
*
* @return
*/
@SneakyThrows
public static String getOAuthClientId() {
public static String getOAuth2ClientId() {
String clientId;
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();