提交 be9db232 编写于 作者: L lbw

Refactoring code. close #I4QEY0 登录失败会加载2次失败处理方法

 Refactoring code. close #I4QEY0  登录失败会加载2次失败处理方法
上级 a6780cee
......@@ -20,7 +20,6 @@ import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.security.component.PigWebResponseExceptionTranslator;
import com.pig4cloud.pig.common.security.grant.ResourceOwnerCustomeAppTokenGranter;
import com.pig4cloud.pig.common.security.service.PigClientDetailsService;
import com.pig4cloud.pig.common.security.service.PigCustomAuthenticationProvider;
import com.pig4cloud.pig.common.security.service.PigCustomTokenServices;
import com.pig4cloud.pig.common.security.service.PigUser;
import lombok.RequiredArgsConstructor;
......@@ -29,7 +28,6 @@ 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.password.PasswordEncoder;
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;
......@@ -83,6 +81,10 @@ public class AuthorizationServerConfiguration extends AuthorizationServerConfigu
setTokenGranter(endpoints);
}
/**
* 自定义 APP 认证类型
* @param endpoints AuthorizationServerEndpointsConfigurer
*/
private void setTokenGranter(AuthorizationServerEndpointsConfigurer endpoints) {
// 获取默认授权类型
TokenGranter tokenGranter = endpoints.getTokenGranter();
......@@ -95,6 +97,10 @@ public class AuthorizationServerConfiguration extends AuthorizationServerConfigu
endpoints.tokenGranter(compositeTokenGranter);
}
/**
* token 生成接口输出增强
* @return TokenEnhancer
*/
@Bean
public TokenEnhancer tokenEnhancer() {
return (accessToken, authentication) -> {
......@@ -116,6 +122,10 @@ public class AuthorizationServerConfiguration extends AuthorizationServerConfigu
};
}
/**
* 客户端信息加载处理
* @return ClientDetailsService
*/
@Bean
public ClientDetailsService pigClientDetailsService() {
PigClientDetailsService clientDetailsService = new PigClientDetailsService(dataSource);
......@@ -124,13 +134,10 @@ public class AuthorizationServerConfiguration extends AuthorizationServerConfigu
return clientDetailsService;
}
@Bean
public PigCustomAuthenticationProvider authenticationProvider(PasswordEncoder passwordEncoder) {
PigCustomAuthenticationProvider authenticationProvider = new PigCustomAuthenticationProvider();
authenticationProvider.setPasswordEncoder(passwordEncoder);
return authenticationProvider;
}
/**
* token 核心处理
* @return tokenServices
*/
@Bean
public PigCustomTokenServices tokenServices() {
PigCustomTokenServices tokenServices = new PigCustomTokenServices();
......
......@@ -19,15 +19,19 @@ package com.pig4cloud.pig.auth.config;
import com.pig4cloud.pig.common.security.grant.CustomAppAuthenticationProvider;
import com.pig4cloud.pig.common.security.handler.FormAuthenticationFailureHandler;
import com.pig4cloud.pig.common.security.handler.SsoLogoutSuccessHandler;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Bean;
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;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
......@@ -35,27 +39,40 @@ import org.springframework.security.web.authentication.logout.LogoutSuccessHandl
/**
* @author lengleng
* @date 2019/2/1 认证相关配置
* @date 2022/1/12 认证相关配置
*/
@Primary
@Order(90)
@Configuration
@RequiredArgsConstructor
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private final UserDetailsService pigUserDetailsService;
@Override
@SneakyThrows
protected void configure(HttpSecurity http) {
http.authenticationProvider(new CustomAppAuthenticationProvider())//
.formLogin().loginPage("/token/login").loginProcessingUrl("/token/form")
http.formLogin().loginPage("/token/login").loginProcessingUrl("/token/form")
.failureHandler(authenticationFailureHandler()).and().logout()
.logoutSuccessHandler(logoutSuccessHandler()).deleteCookies("JSESSIONID").invalidateHttpSession(true)
.and().authorizeRequests().antMatchers("/token/**", "/actuator/**", "/mobile/**").permitAll()
.anyRequest().authenticated().and().csrf().disable();
}
/**
* 自定义 provider 列表注入
* @param auth AuthenticationManagerBuilder
*/
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/css/**");
protected void configure(AuthenticationManagerBuilder auth) {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
daoAuthenticationProvider.setUserDetailsService(pigUserDetailsService);
// 处理默认的密码模式认证
auth.authenticationProvider(daoAuthenticationProvider);
// 自定义的认证模式
auth.authenticationProvider(new CustomAppAuthenticationProvider());
}
@Bean
......@@ -65,13 +82,26 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
return super.authenticationManagerBean();
}
/**
* 认证中心静态资源处理
* @param web WebSecurity
*/
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/css/**");
}
/**
* sso 表单登录失败处理
* @return FormAuthenticationFailureHandler
*/
@Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
return new FormAuthenticationFailureHandler();
}
/**
* 支持SSO 退出
* SSO 退出逻辑处理
* @return LogoutSuccessHandler
*/
@Bean
......@@ -80,9 +110,8 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
}
/**
* https://spring.io/blog/2017/11/01/spring-security-5-0-0-rc1-released#password-storage-updated
* Encoded password does not look like BCrypt
* @return PasswordEncoder
* 密码处理器
* @return 动态密码处理器 {类型}密文
*/
@Bean
public PasswordEncoder passwordEncoder() {
......
package com.pig4cloud.pig.common.security.service;
import cn.hutool.extra.spring.SpringUtil;
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.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 2021/12/21
*
* 自定义 AuthenticationProvider 方便多用户体系
*/
public class PigCustomAuthenticationProvider 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 UserDetailsPasswordService userDetailsPasswordService;
public PigCustomAuthenticationProvider() {
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();
try {
// 此处已获得 客户端认证 获取对应 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));
if (!optional.isPresent()) {
throw new InternalAuthenticationServiceException("UserDetailsService error , not register");
}
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 setUserDetailsPasswordService(UserDetailsPasswordService userDetailsPasswordService) {
this.userDetailsPasswordService = userDetailsPasswordService;
}
}
......@@ -26,6 +26,7 @@ import lombok.SneakyThrows;
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.userdetails.UserDetails;
/**
......@@ -34,6 +35,7 @@ import org.springframework.security.core.userdetails.UserDetails;
* @author lengleng hccake
*/
@Slf4j
@Primary
@RequiredArgsConstructor
public class PigUserDetailsServiceImpl implements PigUserDetailsService {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册