mirror of
https://gitee.com/log4j/pig.git
synced 2024-12-22 12:48:58 +08:00
♻️ Refactoring code. 重构支持多用户体系, SSO 多用户体系
This commit is contained in:
parent
2bee5ba814
commit
fb60fc51ce
@ -33,11 +33,11 @@
|
||||
|
||||
| 依赖 | 版本 |
|
||||
| ---------------------- |----------|
|
||||
| Spring Boot | 2.6.2 |
|
||||
| Spring Boot | 2.6.3 |
|
||||
| Spring Cloud | 2021.0.0 |
|
||||
| Spring Cloud Alibaba | 2021.1 |
|
||||
| Spring Security OAuth2 | 2.3.6 |
|
||||
| Mybatis Plus | 3.5.0 |
|
||||
| Mybatis Plus | 3.5.1 |
|
||||
| hutool | 5.7.19 |
|
||||
| Avue | 2.6.18 |
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.pig4cloud.pig.auth.config;
|
||||
|
||||
import com.pig4cloud.pig.common.security.component.PigDaoAuthenticationProvider;
|
||||
import com.pig4cloud.pig.common.security.grant.CustomAppAuthenticationProvider;
|
||||
import com.pig4cloud.pig.common.security.handler.FormAuthenticationFailureHandler;
|
||||
import com.pig4cloud.pig.common.security.handler.SsoLogoutSuccessHandler;
|
||||
@ -26,7 +27,6 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
@ -65,9 +65,8 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
*/
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) {
|
||||
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
|
||||
PigDaoAuthenticationProvider daoAuthenticationProvider = new PigDaoAuthenticationProvider();
|
||||
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
|
||||
daoAuthenticationProvider.setUserDetailsService(pigUserDetailsServiceImpl);
|
||||
|
||||
// 处理默认的密码模式认证
|
||||
auth.authenticationProvider(daoAuthenticationProvider);
|
||||
|
@ -18,6 +18,7 @@
|
||||
<div class="container form-margin-top">
|
||||
<form class="form-signin" action="/token/form" method="post">
|
||||
<h2 class="form-signin-heading" align="center">统一认证系统</h2>
|
||||
<input type="hidden" name="clientId" class="form-control" value="pig" placeholder="所属客户端" >
|
||||
<input type="text" name="username" class="form-control form-margin-top" placeholder="账号" required autofocus>
|
||||
<input type="password" name="password" class="form-control" placeholder="密码" required>
|
||||
<button class="btn btn-lg btn-primary btn-block" type="submit">sign in</button>
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
<properties>
|
||||
<pig.common.version>${project.version}</pig.common.version>
|
||||
<spring-boot.version>2.6.2</spring-boot.version>
|
||||
<spring-boot.version>2.6.3</spring-boot.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<security.oauth.version>2.1.8.RELEASE</security.oauth.version>
|
||||
<log4j2.version>2.17.1</log4j2.version>
|
||||
@ -25,7 +25,7 @@
|
||||
<spring.checkstyle.plugin>0.0.29</spring.checkstyle.plugin>
|
||||
<fastjson.version>1.2.78</fastjson.version>
|
||||
<swagger.core.version>1.5.24</swagger.core.version>
|
||||
<mybatis-plus.version>3.5.0</mybatis-plus.version>
|
||||
<mybatis-plus.version>3.5.1</mybatis-plus.version>
|
||||
<nacos.version>2.0.3</nacos.version>
|
||||
<excel.version>1.1.0</excel.version>
|
||||
<oss.version>1.0.3</oss.version>
|
||||
|
@ -0,0 +1,174 @@
|
||||
package com.pig4cloud.pig.common.security.component;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.pig4cloud.pig.common.core.util.WebUtils;
|
||||
import com.pig4cloud.pig.common.security.service.PigUserDetailsService;
|
||||
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 PigDaoAuthenticationProvider 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 PigDaoAuthenticationProvider() {
|
||||
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().get().getParameter("clientId");
|
||||
}
|
||||
else {
|
||||
clientId = clientAuthentication.getName();
|
||||
}
|
||||
|
||||
Map<String, PigUserDetailsService> userDetailsServiceMap = SpringUtil
|
||||
.getBeansOfType(PigUserDetailsService.class);
|
||||
Optional<PigUserDetailsService> 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;
|
||||
}
|
||||
|
||||
}
|
@ -50,7 +50,8 @@ public class PigLocalResourceServerTokenServices implements ResourceServerTokenS
|
||||
Map<String, PigUserDetailsService> userDetailsServiceMap = SpringUtil
|
||||
.getBeansOfType(PigUserDetailsService.class);
|
||||
Optional<PigUserDetailsService> optional = userDetailsServiceMap.values().stream()
|
||||
.filter(service -> service.support(clientId)).max(Comparator.comparingInt(Ordered::getOrder));
|
||||
.filter(service -> service.support(clientId, oAuth2Request.getGrantType()))
|
||||
.max(Comparator.comparingInt(Ordered::getOrder));
|
||||
|
||||
if (!optional.isPresent()) {
|
||||
throw new InternalAuthenticationServiceException("UserDetailsService error , not register");
|
||||
|
@ -59,13 +59,16 @@ public class CustomAppAuthenticationProvider extends AbstractUserDetailsAuthenti
|
||||
throw new BadCredentialsException("Bad credentials");
|
||||
}
|
||||
|
||||
CustomAppAuthenticationToken requestToken = (CustomAppAuthenticationToken) authentication;
|
||||
|
||||
// 此处已获得 客户端认证 获取对应 userDetailsService
|
||||
Authentication clientAuthentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
String clientId = clientAuthentication.getName();
|
||||
Map<String, PigUserDetailsService> userDetailsServiceMap = SpringUtil
|
||||
.getBeansOfType(PigUserDetailsService.class);
|
||||
Optional<PigUserDetailsService> optional = userDetailsServiceMap.values().stream()
|
||||
.filter(service -> service.support(clientId)).max(Comparator.comparingInt(Ordered::getOrder));
|
||||
.filter(service -> service.support(clientId, requestToken.getGrantType()))
|
||||
.max(Comparator.comparingInt(Ordered::getOrder));
|
||||
|
||||
if (!optional.isPresent()) {
|
||||
throw new InternalAuthenticationServiceException("UserDetailsService error , not register");
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.pig4cloud.pig.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;
|
||||
@ -15,10 +16,17 @@ public class CustomAppAuthenticationToken extends AbstractAuthenticationToken {
|
||||
// 验证码/密码
|
||||
private String code;
|
||||
|
||||
public CustomAppAuthenticationToken(String phone, 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) {
|
||||
|
@ -56,7 +56,7 @@ public class ResourceOwnerCustomeAppTokenGranter extends AbstractTokenGranter {
|
||||
// Protect from downstream leaks of code
|
||||
parameters.remove("code");
|
||||
|
||||
Authentication userAuth = new CustomAppAuthenticationToken(mobile, code);
|
||||
Authentication userAuth = new CustomAppAuthenticationToken(mobile, code, tokenRequest.getGrantType());
|
||||
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
|
||||
try {
|
||||
userAuth = authenticationManager.authenticate(userAuth);
|
||||
|
@ -79,8 +79,8 @@ public class PigAppUserDetailsServiceImpl implements PigUserDetailsService {
|
||||
* @return true/false
|
||||
*/
|
||||
@Override
|
||||
public boolean support(String clientId) {
|
||||
return "app".equals(clientId);
|
||||
public boolean support(String clientId, String grantType) {
|
||||
return SecurityConstants.APP.equals(grantType);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ public interface PigUserDetailsService extends UserDetailsService, Ordered {
|
||||
* @param clientId 目标客户端
|
||||
* @return true/false
|
||||
*/
|
||||
default boolean support(String clientId) {
|
||||
default boolean support(String clientId, String grantType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user