新增springsecurity短信支持

This commit is contained in:
阿沐 2022-08-30 22:36:43 +08:00
parent 9e515b1d62
commit b5010197d3
5 changed files with 214 additions and 9 deletions

View File

@ -2,43 +2,74 @@ package net.maku.framework.security.config;
import lombok.AllArgsConstructor;
import net.maku.framework.security.exception.SecurityAuthenticationEntryPoint;
import net.maku.framework.security.mobile.MobileAuthenticationProvider;
import net.maku.framework.security.mobile.MobileUserDetailsService;
import net.maku.framework.security.mobile.MobileVerifyCodeService;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.OncePerRequestFilter;
import java.util.ArrayList;
import java.util.List;
/**
* SpringSecurity 配置文件
*
* @author 阿沐 babamu@126.com
*/
@Configuration
@AllArgsConstructor
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
private final UserDetailsService userDetailsService;
private final OncePerRequestFilter authenticationTokenFilter;
private final PermitResource permitResource;
private final UserDetailsService userDetailsService;
private final MobileUserDetailsService mobileUserDetailsService;
private final MobileVerifyCodeService mobileVerifyCodeService;
private final PasswordEncoder passwordEncoder;
private final ApplicationEventPublisher applicationEventPublisher;
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
return daoAuthenticationProvider;
}
@Bean
public AuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
return new DefaultAuthenticationEventPublisher(applicationEventPublisher);
MobileAuthenticationProvider mobileAuthenticationProvider() {
return new MobileAuthenticationProvider(mobileUserDetailsService, mobileVerifyCodeService);
}
@Bean
public AuthenticationManager authenticationManager() {
List<AuthenticationProvider> providerList = new ArrayList<>();
providerList.add(daoAuthenticationProvider());
providerList.add(mobileAuthenticationProvider());
ProviderManager providerManager = new ProviderManager(providerList);
providerManager.setAuthenticationEventPublisher(new DefaultAuthenticationEventPublisher(applicationEventPublisher));
return providerManager;
}
@Bean
@ -53,8 +84,7 @@ public class SecurityConfig {
.and().authorizeRequests()
.antMatchers(permits).permitAll()
.anyRequest().authenticated()
.and().userDetailsService(userDetailsService)
.exceptionHandling().authenticationEntryPoint(new SecurityAuthenticationEntryPoint())
.and().exceptionHandling().authenticationEntryPoint(new SecurityAuthenticationEntryPoint())
.and().headers().frameOptions().disable()
.and().csrf().disable()
;

View File

@ -0,0 +1,87 @@
package net.maku.framework.security.mobile;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
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.AuthenticationException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.Assert;
/**
* 手机短信登录 AuthenticationProvider
*
* @author 阿沐 babamu@126.com
*/
public class MobileAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private final GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
private final MobileUserDetailsService mobileUserDetailsService;
private final MobileVerifyCodeService mobileVerifyCodeService;
public MobileAuthenticationProvider(MobileUserDetailsService mobileUserDetailsService, MobileVerifyCodeService mobileVerifyCodeService) {
this.mobileUserDetailsService = mobileUserDetailsService;
this.mobileVerifyCodeService = mobileVerifyCodeService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Assert.isInstanceOf(MobileAuthenticationToken.class, authentication,
() -> messages.getMessage(
"MobileAuthenticationProvider.onlySupports",
"Only MobileAuthenticationProvider is supported"));
MobileAuthenticationToken authenticationToken = (MobileAuthenticationToken) authentication;
String mobile = authenticationToken.getName();
String code = (String) authenticationToken.getCredentials();
try {
UserDetails userDetails = mobileUserDetailsService.loadUserByMobile(mobile);
if (userDetails == null) {
throw new BadCredentialsException("Bad credentials");
}
// 短信验证码效验
if (mobileVerifyCodeService.verifyCode(mobile, code)) {
return createSuccessAuthentication(authentication, userDetails);
} else {
throw new BadCredentialsException("mobile code is not matched");
}
} catch (UsernameNotFoundException ex) {
throw new BadCredentialsException(this.messages
.getMessage("MobileAuthenticationProvider.badCredentials", "Bad credentials"));
}
}
protected Authentication createSuccessAuthentication(Authentication authentication, UserDetails user) {
MobileAuthenticationToken result = new MobileAuthenticationToken(user, null,
authoritiesMapper.mapAuthorities(user.getAuthorities()));
result.setDetails(authentication.getDetails());
return result;
}
@Override
public boolean supports(Class<?> authentication) {
return MobileAuthenticationToken.class.isAssignableFrom(authentication);
}
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(mobileUserDetailsService, "mobileUserDetailsService must not be null");
Assert.notNull(mobileVerifyCodeService, "mobileVerifyCodeService must not be null");
}
@Override
public void setMessageSource(MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
}

View File

@ -0,0 +1,56 @@
package net.maku.framework.security.mobile;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.util.Assert;
import java.util.Collection;
/**
* 手机短信登录 AuthenticationToken
*
* @author 阿沐 babamu@126.com
*/
public class MobileAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final Object principal;
private String code;
public MobileAuthenticationToken(Object principal, String code) {
super(null);
this.principal = principal;
this.code = code;
setAuthenticated(false);
}
public MobileAuthenticationToken(Object principal, String code, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.code = code;
super.setAuthenticated(true);
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
Assert.isTrue(!isAuthenticated,
"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
super.setAuthenticated(false);
}
@Override
public Object getCredentials() {
return this.code;
}
@Override
public Object getPrincipal() {
return this.principal;
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
this.code = null;
}
}

View File

@ -0,0 +1,21 @@
package net.maku.framework.security.mobile;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
* 手机短信登录UserDetailsService
*
* @author 阿沐 babamu@126.com
*/
public interface MobileUserDetailsService {
/**
* 通过手机号加载用户信息
*
* @param mobile 手机号
* @return 用户信息
* @throws UsernameNotFoundException 不存在异常
*/
UserDetails loadUserByMobile(String mobile) throws UsernameNotFoundException;
}

View File

@ -0,0 +1,11 @@
package net.maku.framework.security.mobile;
/**
* 手机短信登录验证码效验
*
* @author 阿沐 babamu@126.com
*/
public interface MobileVerifyCodeService {
boolean verifyCode(String mobile, String code);
}