1.创建用户实体类CustomUserDetails:
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Collections;
public class CustomUserDetails implements UserDetails {
private String username;
private String password;
private boolean enabled;
private String phoneNumber;
private String email;
public CustomUserDetails(String username, String password, boolean enabled, String phoneNumber, String email) {
this.username = username;
this.password = password;
this.enabled = enabled;
this.phoneNumber = phoneNumber;
this.email = email;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// 返回用户权限列表,这里可以根据需要进行设置
return Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"));
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enabled;
}
// 添加获取手机号和邮箱的方法
public String getPhoneNumber() {
return phoneNumber;
}
public String getEmail() {
return email;
}
}
2.实现自定义的UserDetailsService:
@Service
public class CustomUserDetailsService implements UserDetailsService {
// 假设用户信息硬编码
private Map<String, CustomUserDetails> users = new HashMap<>();
public CustomUserDetailsService() {
// 在这里初始化用户信息,可以从数据库或其他数据源加载
// 假设我们有两个用户:user1和user2
CustomUserDetails user1 = new CustomUserDetails("user1", "$2a$10$E4eDqU4K0ZxL/bDVkTZ8/O0NV5kwq/jK42noRgHh6Xn7gZImkNSxG", true, "123456789", "user1@example.com");
CustomUserDetails user2 = new CustomUserDetails("user2", "$2a$10$2ncJ8j.mF/7soi/Zk3SGLOxnwAmP0NVklmz2ZI8KU1KifVK6yoB1O", true, "987654321", "user2@example.com");
users.put("user1", user1);
users.put("user2", user2);
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 从用户列表中查找用户信息
CustomUserDetails userDetails = users.get(username);
if (userDetails == null) {
throw new UsernameNotFoundException("User not found");
}
return userDetails;
}
}
3.创建认证令牌类:
public class SmsAuthenticationToken extends AbstractAuthenticationToken {
private final String phoneNumber;
private final String smsCode;
public SmsAuthenticationToken(String phoneNumber, String smsCode) {
super(null);
this.phoneNumber = phoneNumber;
this.smsCode = smsCode;
}
@Override
public Object getCredentials() {
return smsCode;
}
@Override
public Object getPrincipal() {
return phoneNumber;
}
}
public class EmailAuthenticationToken extends AbstractAuthenticationToken {
private final String email;
private final String emailCode;
public EmailAuthenticationToken(String email, String emailCode) {
super(null);
this.email = email;
this.emailCode = emailCode;
}
@Override
public Object getCredentials() {
return emailCode;
}
@Override
public Object getPrincipal() {
return email;
}
}
4.实现自定义的认证过滤器:
public class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public SmsAuthenticationFilter() {
super(new AntPathRequestMatcher("/authenticate/sms", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
String phoneNumber = obtainPhoneNumber(request);
String smsCode = obtainSmsCode(request);
if (phoneNumber == null) {
phoneNumber = "";
}
if (smsCode == null) {
smsCode = "";
}
phoneNumber = phoneNumber.trim();
SmsAuthenticationToken authRequest = new SmsAuthenticationToken(phoneNumber, smsCode);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
private String obtainPhoneNumber(HttpServletRequest request) {
// 从请求中获取手机号码
return request.getParameter("phoneNumber");
}
private String obtainSmsCode(HttpServletRequest request) {
// 从请求中获取短信验证码
return request.getParameter("smsCode");
}
private void setDetails(HttpServletRequest request, SmsAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
}
public class EmailAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public EmailAuthenticationFilter() {
super(new AntPathRequestMatcher("/authenticate/email", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
String email = obtainEmail(request);
String emailCode = obtainEmailCode(request);
if (email == null) {
email = "";
}
if (emailCode == null) {
emailCode = "";
}
email = email.trim();
EmailAuthenticationToken authRequest = new EmailAuthenticationToken(email, emailCode);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
private String obtainEmail(HttpServletRequest request) {
// 从请求中获取邮箱
return request.getParameter("email");
}
private String obtainEmailCode(HttpServletRequest request) {
// 从请求中获取邮箱验证码
return request.getParameter("emailCode");
}
private void setDetails(HttpServletRequest request, EmailAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
}
5.实现自定义的AuthenticationProvider:
public class SmsAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String phoneNumber = authentication.getPrincipal().toString();
String smsCode = authentication.getCredentials().toString();
// 在这里实现短信验证的逻辑,根据手机号码和验证码进行验证
UserDetails userDetails = userDetailsService.loadUserByUsername(phoneNumber);
// 验证通过,创建认证后的Token
SmsAuthenticationToken authenticatedToken = new SmsAuthenticationToken(phoneNumber, smsCode, userDetails.getAuthorities());
authenticatedToken.setDetails(authentication.getDetails());
return authenticatedToken;
}
@Override
public boolean supports(Class<?> authentication) {
return SmsAuthenticationToken.class.isAssignableFrom(authentication);
}
}
public class EmailAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String email = authentication.getPrincipal().toString();
String emailCode = authentication.getCredentials().toString();
// 在这里实现邮箱验证的逻辑,根据邮箱和验证码进行验证
UserDetails userDetails = userDetailsService.loadUserByUsername(email);
// 验证通过,创建认证后的Token
EmailAuthenticationToken authenticatedToken = new EmailAuthenticationToken(email, emailCode, userDetails.getAuthorities());
authenticatedToken.setDetails(authentication.getDetails());
return authenticatedToken;
}
@Override
public boolean supports(Class<?> authentication) {
return EmailAuthenticationToken.class.isAssignableFrom(authentication);
}
}
6.配置类中的注册:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(smsAuthenticationProvider());
auth.authenticationProvider(emailAuthenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/authenticate") // 登录表单提交的URL
.defaultSuccessUrl("/home") // 登录成功后跳转的URL
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.and()
.csrf().disable(); // 禁用CSRF,便于测试
// 注册自定义的过滤器
http.addFilterBefore(smsAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(emailAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
public SmsAuthenticationFilter smsAuthenticationFilter() throws Exception {
SmsAuthenticationFilter filter = new SmsAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}
@Bean
public EmailAuthenticationFilter emailAuthenticationFilter() throws Exception {
EmailAuthenticationFilter filter = new EmailAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}
@Bean
public SmsAuthenticationProvider smsAuthenticationProvider() {
return new SmsAuthenticationProvider();
}
@Bean
public EmailAuthenticationProvider emailAuthenticationProvider() {
return new EmailAuthenticationProvider();
}
}