♻️ 重构 close #3472 【用户多表存储】支持多用户体系设置
This commit is contained in:
1 parent
2d2a995a83
commit
bbe1917e01
16 files changed
+622
-412
No files matched your search
+28
-2
@@ -22,6 +22,7 @@ package com.pig4cloud.pigx.auth.config;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.pig4cloud.pigx.common.security.component.PigxCommenceAuthExceptionEntryPoint;
|
||||
import com.pig4cloud.pigx.common.security.component.PigxWebResponseExceptionTranslator;
|
||||
import com.pig4cloud.pigx.common.security.grant.ResourceOwnerCustomeAppTokenGranter;
|
||||
import com.pig4cloud.pigx.common.security.service.PigxCustomTokenServices;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
@@ -38,11 +39,15 @@ 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.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
|
||||
import org.springframework.security.oauth2.provider.ClientDetailsService;
|
||||
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
|
||||
import org.springframework.security.oauth2.provider.TokenGranter;
|
||||
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
|
||||
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
|
||||
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
@@ -52,7 +57,7 @@ import java.util.Collections;
|
||||
@Configuration
|
||||
@AllArgsConstructor
|
||||
@EnableAuthorizationServer
|
||||
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
|
||||
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
|
||||
|
||||
private final ClientDetailsService pigxClientDetailsServiceImpl;
|
||||
|
||||
@@ -62,6 +67,8 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
|
||||
|
||||
private final AuthorizationCodeServices authorizationCodeServices;
|
||||
|
||||
private final AuthenticationManager authenticationManager;
|
||||
|
||||
private final TokenStore redisTokenStore;
|
||||
|
||||
private final TokenEnhancer tokenEnhancer;
|
||||
@@ -84,10 +91,29 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
|
||||
@Override
|
||||
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
|
||||
endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST).tokenServices(tokenServices())
|
||||
.tokenStore(redisTokenStore).tokenEnhancer(tokenEnhancer).userDetailsService(pigxUserDetailsService)
|
||||
.tokenStore(redisTokenStore).tokenEnhancer(tokenEnhancer)
|
||||
.authorizationCodeServices(authorizationCodeServices).authenticationManager(authenticationManagerBean)
|
||||
.reuseRefreshTokens(false).pathMapping("/oauth/confirm_access", "/token/confirm_access")
|
||||
.exceptionTranslator(new PigxWebResponseExceptionTranslator());
|
||||
|
||||
// 注入自定义认证类型
|
||||
setTokenGranter(endpoints);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义 APP 认证类型
|
||||
* @param endpoints AuthorizationServerEndpointsConfigurer
|
||||
*/
|
||||
private void setTokenGranter(AuthorizationServerEndpointsConfigurer endpoints) {
|
||||
// 获取默认授权类型
|
||||
TokenGranter tokenGranter = endpoints.getTokenGranter();
|
||||
ArrayList<TokenGranter> tokenGranters = new ArrayList<>(Arrays.asList(tokenGranter));
|
||||
ResourceOwnerCustomeAppTokenGranter resourceOwnerCustomeAppTokenGranter = new ResourceOwnerCustomeAppTokenGranter(
|
||||
authenticationManager, endpoints.getTokenServices(), endpoints.getClientDetailsService(),
|
||||
endpoints.getOAuth2RequestFactory());
|
||||
tokenGranters.add(resourceOwnerCustomeAppTokenGranter);
|
||||
CompositeTokenGranter compositeTokenGranter = new CompositeTokenGranter(tokenGranters);
|
||||
endpoints.tokenGranter(compositeTokenGranter);
|
||||
}
|
||||
|
||||
@Bean
|
||||
+2
-12
@@ -91,19 +91,9 @@ public interface SecurityConstants {
|
||||
String OAUTH_TOKEN_URL = "/oauth/token";
|
||||
|
||||
/**
|
||||
* 手机号登录URL
|
||||
* 移动端授权
|
||||
*/
|
||||
String SMS_TOKEN_URL = "/mobile/token/sms";
|
||||
|
||||
/**
|
||||
* 社交登录URL
|
||||
*/
|
||||
String SOCIAL_TOKEN_URL = "/mobile/token/social";
|
||||
|
||||
/**
|
||||
* 自定义登录URL
|
||||
*/
|
||||
String MOBILE_TOKEN_URL = "/mobile/token/*";
|
||||
String GRANT_MOBILE = "mobile";
|
||||
|
||||
/**
|
||||
* 微信获取OPENID
|
||||
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
package com.pig4cloud.pigx.common.security.component;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.pig4cloud.pigx.common.core.util.WebUtils;
|
||||
import com.pig4cloud.pigx.common.security.service.PigxUserDetailsService;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2022/1/15
|
||||
*/
|
||||
public class PigxDaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
|
||||
|
||||
/**
|
||||
* The plaintext password used to perform PasswordEncoder#matches(CharSequence,
|
||||
* String)} on when the user is not found to avoid SEC-2056.
|
||||
*/
|
||||
private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
|
||||
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
/**
|
||||
* The password used to perform {@link PasswordEncoder#matches(CharSequence, String)}
|
||||
* on when the user is not found to avoid SEC-2056. This is necessary, because some
|
||||
* {@link PasswordEncoder} implementations will short circuit if the password is not
|
||||
* in a valid format.
|
||||
*/
|
||||
private volatile String userNotFoundEncodedPassword;
|
||||
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
private UserDetailsPasswordService userDetailsPasswordService;
|
||||
|
||||
public PigxDaoAuthenticationProvider() {
|
||||
setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void additionalAuthenticationChecks(UserDetails userDetails,
|
||||
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
|
||||
if (authentication.getCredentials() == null) {
|
||||
this.logger.debug("Failed to authenticate since no credentials provided");
|
||||
throw new BadCredentialsException(this.messages
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
}
|
||||
String presentedPassword = authentication.getCredentials().toString();
|
||||
if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
|
||||
this.logger.debug("Failed to authenticate since password does not match stored value");
|
||||
throw new BadCredentialsException(this.messages
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
|
||||
throws AuthenticationException {
|
||||
prepareTimingAttackProtection();
|
||||
|
||||
// 此处已获得 客户端认证 获取对应 userDetailsService
|
||||
Authentication clientAuthentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
// SSO NPE 处理
|
||||
String clientId;
|
||||
if (clientAuthentication == null) {
|
||||
clientId = WebUtils.getRequest().getParameter("clientId");
|
||||
}
|
||||
else {
|
||||
clientId = clientAuthentication.getName();
|
||||
}
|
||||
|
||||
Map<String, PigxUserDetailsService> userDetailsServiceMap = SpringUtil
|
||||
.getBeansOfType(PigxUserDetailsService.class);
|
||||
Optional<PigxUserDetailsService> optional = userDetailsServiceMap.values().stream()
|
||||
.filter(service -> service.support(clientId, null)).max(Comparator.comparingInt(Ordered::getOrder));
|
||||
|
||||
if (!optional.isPresent()) {
|
||||
throw new InternalAuthenticationServiceException("UserDetailsService error , not register");
|
||||
}
|
||||
|
||||
try {
|
||||
UserDetails loadedUser = optional.get().loadUserByUsername(username);
|
||||
if (loadedUser == null) {
|
||||
throw new InternalAuthenticationServiceException(
|
||||
"UserDetailsService returned null, which is an interface contract violation");
|
||||
}
|
||||
return loadedUser;
|
||||
}
|
||||
catch (UsernameNotFoundException ex) {
|
||||
mitigateAgainstTimingAttack(authentication);
|
||||
throw ex;
|
||||
}
|
||||
catch (InternalAuthenticationServiceException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Authentication createSuccessAuthentication(Object principal, Authentication authentication,
|
||||
UserDetails user) {
|
||||
boolean upgradeEncoding = this.userDetailsPasswordService != null
|
||||
&& this.passwordEncoder.upgradeEncoding(user.getPassword());
|
||||
if (upgradeEncoding) {
|
||||
String presentedPassword = authentication.getCredentials().toString();
|
||||
String newPassword = this.passwordEncoder.encode(presentedPassword);
|
||||
user = this.userDetailsPasswordService.updatePassword(user, newPassword);
|
||||
}
|
||||
return super.createSuccessAuthentication(principal, authentication, user);
|
||||
}
|
||||
|
||||
private void prepareTimingAttackProtection() {
|
||||
if (this.userNotFoundEncodedPassword == null) {
|
||||
this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD);
|
||||
}
|
||||
}
|
||||
|
||||
private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) {
|
||||
if (authentication.getCredentials() != null) {
|
||||
String presentedPassword = authentication.getCredentials().toString();
|
||||
this.passwordEncoder.matches(presentedPassword, this.userNotFoundEncodedPassword);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the PasswordEncoder instance to be used to encode and validate passwords. If
|
||||
* not set, the password will be compared using
|
||||
* {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}
|
||||
* @param passwordEncoder must be an instance of one of the {@code PasswordEncoder}
|
||||
* types.
|
||||
*/
|
||||
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
|
||||
Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.userNotFoundEncodedPassword = null;
|
||||
}
|
||||
|
||||
protected PasswordEncoder getPasswordEncoder() {
|
||||
return this.passwordEncoder;
|
||||
}
|
||||
|
||||
public void setUserDetailsService(UserDetailsService userDetailsService) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
protected UserDetailsService getUserDetailsService() {
|
||||
return this.userDetailsService;
|
||||
}
|
||||
|
||||
public void setUserDetailsPasswordService(UserDetailsPasswordService userDetailsPasswordService) {
|
||||
this.userDetailsPasswordService = userDetailsPasswordService;
|
||||
}
|
||||
|
||||
}
|
||||
+24
-6
@@ -1,14 +1,17 @@
|
||||
package com.pig4cloud.pigx.common.security.component;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.pig4cloud.pigx.common.security.exception.UnauthorizedException;
|
||||
import com.pig4cloud.pigx.common.security.service.PigxUser;
|
||||
import com.pig4cloud.pigx.common.security.service.PigxUserDetailsService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
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.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
|
||||
@@ -18,6 +21,10 @@ import org.springframework.security.oauth2.provider.token.ResourceServerTokenSer
|
||||
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2020/9/29
|
||||
@@ -29,8 +36,6 @@ public class PigxLocalResourceServerTokenServices implements ResourceServerToken
|
||||
|
||||
private final TokenStore tokenStore;
|
||||
|
||||
private final UserDetailsService userDetailsService;
|
||||
|
||||
@Override
|
||||
public OAuth2Authentication loadAuthentication(String accessToken)
|
||||
throws AuthenticationException, InvalidTokenException {
|
||||
@@ -44,15 +49,28 @@ public class PigxLocalResourceServerTokenServices implements ResourceServerToken
|
||||
return oAuth2Authentication;
|
||||
}
|
||||
|
||||
String clientId = oAuth2Request.getClientId();
|
||||
|
||||
Map<String, PigxUserDetailsService> userDetailsServiceMap = SpringUtil
|
||||
.getBeansOfType(PigxUserDetailsService.class);
|
||||
|
||||
Optional<PigxUserDetailsService> optional = userDetailsServiceMap.values().stream()
|
||||
.filter(service -> service.support(clientId, oAuth2Request.getGrantType()))
|
||||
.max(Comparator.comparingInt(Ordered::getOrder));
|
||||
|
||||
if (!optional.isPresent()) {
|
||||
throw new InternalAuthenticationServiceException("UserDetailsService error , not register");
|
||||
}
|
||||
|
||||
// 根据 username 查询 spring cache 最新的值 并返回
|
||||
PigxUser pigxUser = (PigxUser) oAuth2Authentication.getPrincipal();
|
||||
PigxUser pigUser = (PigxUser) oAuth2Authentication.getPrincipal();
|
||||
|
||||
UserDetails userDetails;
|
||||
try {
|
||||
userDetails = userDetailsService.loadUserByUsername(pigxUser.getUsername());
|
||||
userDetails = optional.get().loadUserByUser(pigUser);
|
||||
}
|
||||
catch (UsernameNotFoundException notFoundException) {
|
||||
throw new UnauthorizedException(String.format("%s username not found", pigxUser.getUsername()),
|
||||
throw new UnauthorizedException(String.format("%s username not found", pigUser.getUsername()),
|
||||
notFoundException);
|
||||
}
|
||||
Authentication userAuthentication = new UsernamePasswordAuthenticationToken(userDetails, "N/A",
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package com.pig4cloud.pigx.common.security.grant;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.pig4cloud.pigx.common.security.service.PigxUserDetailsService;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.security.authentication.AccountStatusUserDetailsChecker;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsChecker;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author hzq
|
||||
* @since 2021-09-14
|
||||
*/
|
||||
@Slf4j
|
||||
public class CustomAppAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
|
||||
|
||||
/**
|
||||
* user 属性校验
|
||||
*/
|
||||
@Setter
|
||||
private UserDetailsChecker preAuthenticationChecks = new AccountStatusUserDetailsChecker();
|
||||
|
||||
/**
|
||||
* 校验 请求信息userDetails
|
||||
* @param userDetails 用户信息
|
||||
* @param authentication 认证信息
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
@Override
|
||||
protected void additionalAuthenticationChecks(UserDetails userDetails,
|
||||
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
|
||||
|
||||
if (authentication.getCredentials() == null) {
|
||||
this.logger.debug("Failed to authenticate since no credentials provided");
|
||||
throw new BadCredentialsException(this.messages
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
|
||||
if (authentication.getCredentials() == null) {
|
||||
log.debug("Failed to authenticate since no credentials provided");
|
||||
throw new BadCredentialsException("Bad credentials");
|
||||
}
|
||||
|
||||
CustomAppAuthenticationToken requestToken = (CustomAppAuthenticationToken) authentication;
|
||||
// 此处已获得 客户端认证 获取对应 userDetailsService
|
||||
Authentication clientAuthentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
String clientId = clientAuthentication.getName();
|
||||
|
||||
Map<String, PigxUserDetailsService> userDetailsServiceMap = SpringUtil
|
||||
.getBeansOfType(PigxUserDetailsService.class);
|
||||
Optional<PigxUserDetailsService> optional = userDetailsServiceMap.values().stream()
|
||||
.filter(service -> service.support(clientId, requestToken.getGrantType()))
|
||||
.max(Comparator.comparingInt(Ordered::getOrder));
|
||||
|
||||
if (!optional.isPresent()) {
|
||||
throw new InternalAuthenticationServiceException("UserDetailsService error , not register");
|
||||
}
|
||||
|
||||
// 手机号
|
||||
String phone = authentication.getName();
|
||||
UserDetails userDetails = optional.get().loadUserByUsername(phone);
|
||||
|
||||
// userDeails 校验
|
||||
preAuthenticationChecks.check(userDetails);
|
||||
|
||||
CustomAppAuthenticationToken token = new CustomAppAuthenticationToken(userDetails);
|
||||
token.setDetails(authentication.getDetails());
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UserDetails retrieveUser(String phone, UsernamePasswordAuthenticationToken authentication)
|
||||
throws AuthenticationException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return authentication.isAssignableFrom(CustomAppAuthenticationToken.class);
|
||||
}
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.pig4cloud.pigx.common.security.grant;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
/**
|
||||
* @author hzq
|
||||
* @since 2021-09-14
|
||||
*/
|
||||
public class CustomAppAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private final Object principal;
|
||||
|
||||
/**
|
||||
* 验证码/密码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 授权类型
|
||||
*/
|
||||
@Getter
|
||||
private String grantType;
|
||||
|
||||
public CustomAppAuthenticationToken(String phone, String code, String grantType) {
|
||||
super(AuthorityUtils.NO_AUTHORITIES);
|
||||
this.principal = phone;
|
||||
this.code = code;
|
||||
this.grantType = grantType;
|
||||
}
|
||||
|
||||
public CustomAppAuthenticationToken(UserDetails sysUser) {
|
||||
super(sysUser.getAuthorities());
|
||||
this.principal = sysUser;
|
||||
super.setAuthenticated(true); // 设置认证成功 必须
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package com.pig4cloud.pigx.common.security.grant;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.authentication.AccountStatusException;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
|
||||
import org.springframework.security.oauth2.provider.*;
|
||||
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
|
||||
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 资源所有者电话令牌授予者
|
||||
*
|
||||
* @author hzq
|
||||
* @since 2021-09-14
|
||||
*/
|
||||
public class ResourceOwnerCustomeAppTokenGranter extends AbstractTokenGranter {
|
||||
|
||||
private static final String GRANT_TYPE = "mobile";
|
||||
|
||||
private final AuthenticationManager authenticationManager;
|
||||
|
||||
public ResourceOwnerCustomeAppTokenGranter(AuthenticationManager authenticationManager,
|
||||
AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService,
|
||||
OAuth2RequestFactory requestFactory) {
|
||||
this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
|
||||
}
|
||||
|
||||
protected ResourceOwnerCustomeAppTokenGranter(AuthenticationManager authenticationManager,
|
||||
AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService,
|
||||
OAuth2RequestFactory requestFactory, String grantType) {
|
||||
super(tokenServices, clientDetailsService, requestFactory, grantType);
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
|
||||
|
||||
Map<String, String> parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters());
|
||||
|
||||
// 手机号
|
||||
String mobile = parameters.get("mobile");
|
||||
// 验证码/密码
|
||||
String code = parameters.get("code");
|
||||
|
||||
if (StrUtil.isBlank(mobile) || StrUtil.isBlank(code)) {
|
||||
throw new InvalidGrantException("Bad credentials [ params must be has phone with code ]");
|
||||
}
|
||||
|
||||
// Protect from downstream leaks of code
|
||||
parameters.remove("code");
|
||||
|
||||
Authentication userAuth = new CustomAppAuthenticationToken(mobile, code, tokenRequest.getGrantType());
|
||||
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
|
||||
try {
|
||||
userAuth = authenticationManager.authenticate(userAuth);
|
||||
}
|
||||
catch (AccountStatusException | BadCredentialsException ase) {
|
||||
// covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
|
||||
throw new InvalidGrantException(ase.getMessage());
|
||||
}
|
||||
// If the phone/code are wrong the spec says we should send 400/invalid grant
|
||||
|
||||
if (userAuth == null || !userAuth.isAuthenticated()) {
|
||||
throw new InvalidGrantException("Could not authenticate user: " + mobile);
|
||||
}
|
||||
|
||||
OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
|
||||
return new OAuth2Authentication(storedOAuth2Request, userAuth);
|
||||
}
|
||||
|
||||
}
|
||||
-111
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.common.security.mobile;
|
||||
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018/1/9 手机号登录验证filter
|
||||
*/
|
||||
public class MobileAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
|
||||
|
||||
private static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile";
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private String mobileParameter = SPRING_SECURITY_FORM_MOBILE_KEY;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean postOnly = true;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private AuthenticationEntryPoint authenticationEntryPoint;
|
||||
|
||||
public MobileAuthenticationFilter() {
|
||||
super(new AntPathRequestMatcher(SecurityConstants.MOBILE_TOKEN_URL, "POST"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
|
||||
if (postOnly && !request.getMethod().equals(HttpMethod.POST.name())) {
|
||||
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
|
||||
}
|
||||
|
||||
String mobile = obtainMobile(request);
|
||||
|
||||
if (mobile == null) {
|
||||
mobile = "";
|
||||
}
|
||||
|
||||
mobile = mobile.trim();
|
||||
|
||||
MobileAuthenticationToken mobileAuthenticationToken = new MobileAuthenticationToken(mobile);
|
||||
|
||||
setDetails(request, mobileAuthenticationToken);
|
||||
|
||||
Authentication authResult = null;
|
||||
try {
|
||||
authResult = this.getAuthenticationManager().authenticate(mobileAuthenticationToken);
|
||||
|
||||
logger.debug("Authentication success: " + authResult);
|
||||
SecurityContextHolder.getContext().setAuthentication(authResult);
|
||||
|
||||
}
|
||||
catch (Exception failed) {
|
||||
SecurityContextHolder.clearContext();
|
||||
logger.debug("Authentication request failed: " + failed);
|
||||
|
||||
try {
|
||||
authenticationEntryPoint.commence(request, response,
|
||||
new UsernameNotFoundException(failed.getMessage(), failed));
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error("authenticationEntryPoint handle error:{}", failed);
|
||||
}
|
||||
}
|
||||
|
||||
return authResult;
|
||||
}
|
||||
|
||||
private String obtainMobile(HttpServletRequest request) {
|
||||
return request.getParameter(mobileParameter);
|
||||
}
|
||||
|
||||
private void setDetails(HttpServletRequest request, MobileAuthenticationToken authRequest) {
|
||||
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
|
||||
}
|
||||
|
||||
}
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.common.security.mobile;
|
||||
|
||||
import com.pig4cloud.pigx.common.security.component.PigxPreAuthenticationChecks;
|
||||
import com.pig4cloud.pigx.common.security.service.PigxUserDetailsService;
|
||||
import com.pig4cloud.pigx.common.security.util.PigxSecurityMessageSourceUtil;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsChecker;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018/8/5 手机登录校验逻辑 验证码登录、社交登录
|
||||
*/
|
||||
@Slf4j
|
||||
public class MobileAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private MessageSourceAccessor messages = PigxSecurityMessageSourceUtil.getAccessor();
|
||||
|
||||
private UserDetailsChecker detailsChecker = new PigxPreAuthenticationChecks();
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private PigxUserDetailsService userDetailsService;
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public Authentication authenticate(Authentication authentication) {
|
||||
MobileAuthenticationToken mobileAuthenticationToken = (MobileAuthenticationToken) authentication;
|
||||
|
||||
String principal = mobileAuthenticationToken.getPrincipal().toString();
|
||||
UserDetails userDetails = userDetailsService.loadUserBySocial(principal);
|
||||
if (userDetails == null) {
|
||||
log.debug("Authentication failed: no credentials provided");
|
||||
|
||||
throw new BadCredentialsException(messages
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.noopBindAccount", "Noop Bind Account"));
|
||||
}
|
||||
|
||||
// 检查账号状态
|
||||
detailsChecker.check(userDetails);
|
||||
|
||||
MobileAuthenticationToken authenticationToken = new MobileAuthenticationToken(userDetails,
|
||||
userDetails.getAuthorities());
|
||||
authenticationToken.setDetails(mobileAuthenticationToken.getDetails());
|
||||
return authenticationToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return MobileAuthenticationToken.class.isAssignableFrom(authentication);
|
||||
}
|
||||
|
||||
}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.common.security.mobile;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018/1/9 手机号登录令牌
|
||||
*/
|
||||
public class MobileAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final Object principal;
|
||||
|
||||
public MobileAuthenticationToken(String mobile) {
|
||||
super(null);
|
||||
this.principal = mobile;
|
||||
setAuthenticated(false);
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public MobileAuthenticationToken(@JsonProperty("principal") Object principal,
|
||||
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities) {
|
||||
super(authorities);
|
||||
this.principal = principal;
|
||||
super.setAuthenticated(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void setAuthenticated(boolean isAuthenticated) {
|
||||
if (isAuthenticated) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
|
||||
}
|
||||
|
||||
super.setAuthenticated(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eraseCredentials() {
|
||||
super.eraseCredentials();
|
||||
}
|
||||
|
||||
}
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.common.security.mobile;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.pig4cloud.pigx.common.security.component.PigxCommenceAuthExceptionEntryPoint;
|
||||
import com.pig4cloud.pigx.common.security.service.PigxUserDetailsService;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018/8/5 手机号登录配置入口
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class MobileSecurityConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Autowired
|
||||
private AuthenticationSuccessHandler mobileLoginSuccessHandler;
|
||||
|
||||
@Autowired
|
||||
private PigxUserDetailsService userDetailsService;
|
||||
|
||||
@Override
|
||||
public void configure(HttpSecurity http) {
|
||||
MobileAuthenticationFilter mobileAuthenticationFilter = new MobileAuthenticationFilter();
|
||||
mobileAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
|
||||
mobileAuthenticationFilter.setAuthenticationSuccessHandler(mobileLoginSuccessHandler);
|
||||
mobileAuthenticationFilter.setAuthenticationEntryPoint(new PigxCommenceAuthExceptionEntryPoint(objectMapper));
|
||||
|
||||
MobileAuthenticationProvider mobileAuthenticationProvider = new MobileAuthenticationProvider();
|
||||
mobileAuthenticationProvider.setUserDetailsService(userDetailsService);
|
||||
http.authenticationProvider(mobileAuthenticationProvider).addFilterAfter(mobileAuthenticationFilter,
|
||||
UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.common.security.service;
|
||||
|
||||
import com.pig4cloud.pigx.admin.api.dto.UserInfo;
|
||||
import com.pig4cloud.pigx.admin.api.feign.RemoteUserService;
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
|
||||
/**
|
||||
* 用户详细信息
|
||||
*
|
||||
* @author lengleng hccake
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class PigxAppUserDetailsServiceImpl implements PigxUserDetailsService {
|
||||
|
||||
private final UserDetailsService pigxDefaultUserDetailsServiceImpl;
|
||||
|
||||
private final RemoteUserService remoteUserService;
|
||||
|
||||
private final CacheManager cacheManager;
|
||||
|
||||
/**
|
||||
* 手机号登录
|
||||
* @param phone 手机号
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public UserDetails loadUserByUsername(String phone) {
|
||||
Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS);
|
||||
if (cache != null && cache.get(phone) != null) {
|
||||
return (PigxUser) cache.get(phone).get();
|
||||
}
|
||||
|
||||
R<UserInfo> result = remoteUserService.social(phone, SecurityConstants.FROM_IN);
|
||||
|
||||
UserDetails userDetails = getUserDetails(result);
|
||||
if (cache != null) {
|
||||
cache.put(phone, userDetails);
|
||||
}
|
||||
return userDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* check-token 使用
|
||||
* @param pigxUser user
|
||||
* @return UserDetails
|
||||
*/
|
||||
@Override
|
||||
public UserDetails loadUserByUser(PigxUser pigxUser) {
|
||||
return pigxDefaultUserDetailsServiceImpl.loadUserByUsername(pigxUser.getUsername());
|
||||
}
|
||||
|
||||
/**
|
||||
* 支持所有的 mobile 类型
|
||||
* @param clientId 目标客户端
|
||||
* @param grantType 授权类型
|
||||
* @return true/false
|
||||
*/
|
||||
@Override
|
||||
public boolean support(String clientId, String grantType) {
|
||||
return SecurityConstants.GRANT_MOBILE.equals(grantType);
|
||||
}
|
||||
|
||||
}
|
||||
+4
-53
@@ -17,13 +17,9 @@
|
||||
|
||||
package com.pig4cloud.pigx.common.security.service;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pigx.admin.api.dto.UserInfo;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysUser;
|
||||
import com.pig4cloud.pigx.admin.api.feign.RemoteUserService;
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -32,25 +28,18 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 用户详细信息
|
||||
* 用户详细信息 default
|
||||
*
|
||||
* @author lengleng
|
||||
*/
|
||||
@Slf4j
|
||||
@Primary
|
||||
@RequiredArgsConstructor
|
||||
public class PigxUserDetailsServiceImpl implements PigxUserDetailsService {
|
||||
public class PigxDefaultUserDetailsServiceImpl implements PigxUserDetailsService {
|
||||
|
||||
private final RemoteUserService remoteUserService;
|
||||
|
||||
@@ -76,47 +65,9 @@ public class PigxUserDetailsServiceImpl implements PigxUserDetailsService {
|
||||
return userDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据社交登录code 登录
|
||||
* @param inStr TYPE@CODE
|
||||
* @return UserDetails
|
||||
* @throws UsernameNotFoundException
|
||||
*/
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public UserDetails loadUserBySocial(String inStr) {
|
||||
return getUserDetails(remoteUserService.social(inStr, SecurityConstants.FROM_IN));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建userdetails
|
||||
* @param result 用户信息
|
||||
* @return
|
||||
*/
|
||||
private UserDetails getUserDetails(R<UserInfo> result) {
|
||||
if (result == null || result.getData() == null) {
|
||||
throw new UsernameNotFoundException("用户不存在");
|
||||
}
|
||||
|
||||
UserInfo info = result.getData();
|
||||
Set<String> dbAuthsSet = new HashSet<>();
|
||||
if (ArrayUtil.isNotEmpty(info.getRoles())) {
|
||||
// 获取角色
|
||||
Arrays.stream(info.getRoles()).forEach(roleId -> dbAuthsSet.add(SecurityConstants.ROLE + roleId));
|
||||
// 获取资源
|
||||
dbAuthsSet.addAll(Arrays.asList(info.getPermissions()));
|
||||
|
||||
}
|
||||
Collection<? extends GrantedAuthority> authorities = AuthorityUtils
|
||||
.createAuthorityList(dbAuthsSet.toArray(new String[0]));
|
||||
SysUser user = info.getSysUser();
|
||||
boolean enabled = StrUtil.equals(user.getLockFlag(), CommonConstants.STATUS_NORMAL);
|
||||
// 构造security用户
|
||||
|
||||
return new PigxUser(user.getUserId(), user.getUsername(), user.getDeptId(), user.getPhone(), user.getAvatar(),
|
||||
user.getNickname(), user.getName(), user.getEmail(), user.getTenantId(),
|
||||
SecurityConstants.BCRYPT + user.getPassword(), enabled, true, true,
|
||||
!CommonConstants.STATUS_LOCK.equals(user.getLockFlag()), authorities);
|
||||
public int getOrder() {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
}
|
||||
+69
-6
@@ -16,22 +16,85 @@
|
||||
*/
|
||||
package com.pig4cloud.pigx.common.security.service;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import com.pig4cloud.pigx.admin.api.dto.UserInfo;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysUser;
|
||||
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018/8/15
|
||||
*/
|
||||
public interface PigxUserDetailsService extends UserDetailsService {
|
||||
public interface PigxUserDetailsService extends UserDetailsService, Ordered {
|
||||
|
||||
/**
|
||||
* 根据社交登录code 登录
|
||||
* @param code TYPE@CODE
|
||||
* @return UserDetails
|
||||
* @throws UsernameNotFoundException
|
||||
* 是否支持此客户端校验
|
||||
* @param clientId 请求客户端
|
||||
* @param grantType 授权类型
|
||||
* @return true/false
|
||||
*/
|
||||
UserDetails loadUserBySocial(String code) throws UsernameNotFoundException;
|
||||
default boolean support(String clientId, String grantType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 排序值 默认取最大的
|
||||
* @return 排序值
|
||||
*/
|
||||
default int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建userdetails
|
||||
* @param result 用户信息
|
||||
* @return UserDetails
|
||||
*/
|
||||
default UserDetails getUserDetails(R<UserInfo> result) {
|
||||
if (result == null || result.getData() == null) {
|
||||
throw new UsernameNotFoundException("用户不存在");
|
||||
}
|
||||
|
||||
UserInfo info = result.getData();
|
||||
Set<String> dbAuthsSet = new HashSet<>();
|
||||
if (ArrayUtil.isNotEmpty(info.getRoles())) {
|
||||
// 获取角色
|
||||
Arrays.stream(info.getRoles()).forEach(roleId -> dbAuthsSet.add(SecurityConstants.ROLE + roleId));
|
||||
// 获取资源
|
||||
dbAuthsSet.addAll(Arrays.asList(info.getPermissions()));
|
||||
|
||||
}
|
||||
Collection<? extends GrantedAuthority> authorities = AuthorityUtils
|
||||
.createAuthorityList(dbAuthsSet.toArray(new String[0]));
|
||||
SysUser user = info.getSysUser();
|
||||
// 构造security用户
|
||||
|
||||
return new PigxUser(user.getUserId(), user.getUsername(), user.getDeptId(), user.getPhone(), user.getAvatar(),
|
||||
user.getNickname(), user.getName(), user.getEmail(), user.getTenantId(),
|
||||
SecurityConstants.BCRYPT + user.getPassword(), true, true, true,
|
||||
!CommonConstants.STATUS_LOCK.equals(user.getLockFlag()), authorities);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过用户实体查询
|
||||
* @param pigxUser user
|
||||
* @return
|
||||
*/
|
||||
default UserDetails loadUserByUser(PigxUser pigxUser) {
|
||||
return this.loadUserByUsername(pigxUser.getUsername());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.pig4cloud.pigx.common.security.component.PigxTokenEnhancer,\
|
||||
com.pig4cloud.pigx.common.security.component.PigxSecurityInnerAspect,\
|
||||
com.pig4cloud.pigx.common.security.service.PigxUserDetailsServiceImpl,\
|
||||
com.pig4cloud.pigx.common.security.service.PigxDefaultUserDetailsServiceImpl,\
|
||||
com.pig4cloud.pigx.common.security.service.PigxAppUserDetailsServiceImpl,\
|
||||
com.pig4cloud.pigx.common.security.service.PigxClientDetailsServiceImpl,\
|
||||
com.pig4cloud.pigx.common.security.component.PigxTokenStoreAutoConfiguration,\
|
||||
com.pig4cloud.pigx.common.security.component.PigxSecurityMessageSourceConfiguration,\
|
||||
|
||||
+1
-2
@@ -67,8 +67,7 @@ public class ValidateCodeGatewayFilter extends AbstractGatewayFilterFactory {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
|
||||
// 不是账号密码登录、短信登录,直接向下执行
|
||||
if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), SecurityConstants.OAUTH_TOKEN_URL,
|
||||
SecurityConstants.SMS_TOKEN_URL)) {
|
||||
if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), SecurityConstants.OAUTH_TOKEN_URL)) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user