Step 1: 创建自定义UserDetails类
自定义一个实现UserDetails接口的类,用于表示用户信息。您可以在这个类中添加自己需要的属性和方法来存储用户信息。
public class CustomUserDetails implements UserDetails {
private String email;
private boolean enabled;
private List<GrantedAuthority> authorities;
public CustomUserDetails(String email, boolean enabled, List<GrantedAuthority> authorities) {
this.email = email;
this.enabled = enabled;
this.authorities = authorities;
}
// Implement other UserDetails methods...
@Override
public String getUsername() {
return email;
}
// Getters and setters...
}
Step 2: 创建自定义过滤器
创建一个自定义的过滤器,用于处理邮箱验证码登录逻辑。这个过滤器将在用户登录时验证邮箱和验证码,并完成用户认证过程。
public class EmailVerificationAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public EmailVerificationAuthenticationFilter() {
super(new AntPathRequestMatcher("/login/email", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
String email = obtainEmail(request);
String verificationCode = obtainVerificationCode(request);
email = (email != null) ? email.trim() : "";
verificationCode = (verificationCode != null) ? verificationCode.trim() : "";
EmailVerificationToken authRequest = new EmailVerificationToken(email, verificationCode);
return this.getAuthenticationManager().authenticate(authRequest);
}
private String obtainEmail(HttpServletRequest request) {
return request.getParameter("email");
}
private String obtainVerificationCode(HttpServletRequest request) {
return request.getParameter("verificationCode");
}
}
Step 3: 创建自定义认证令牌
创建一个自定义的认证令牌,继承自UsernamePasswordAuthenticationToken,用于封装用户的邮箱和验证码信息。
public class EmailVerificationToken extends UsernamePasswordAuthenticationToken {
private final String email;
private final String verificationCode;
public EmailVerificationToken(String email, String verificationCode) {
super(email, verificationCode);
this.email = email;
this.verificationCode = verificationCode;
}
public String getEmail() {
return email;
}
public String getVerificationCode() {
return verificationCode;
}
}
Step 4: 实现自定义AuthenticationProvider
创建一个自定义的AuthenticationProvider,用于处理邮箱验证登录的认证逻辑。
@Component
public class EmailVerificationAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String email = authentication.getName();
String verificationCode = authentication.getCredentials().toString();
// 在此处编写您的自定义认证逻辑
// 比如根据email和verificationCode验证用户信息
if (authenticationIsValid) {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
CustomUserDetails userDetails = new CustomUserDetails(email, true, authorities);
return new UsernamePasswordAuthenticationToken(userDetails, null, authorities);
} else {
throw new AuthenticationServiceException("Authentication failed");
}
}
@Override
public boolean supports(Class<?> authentication) {
return EmailVerificationToken.class.isAssignableFrom(authentication);
}
}
Step 5: 实现自定义UserDetailsService
创建一个实现UserDetailsService接口的类,用于根据邮箱查询用户信息。
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Autowired
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
// 在此处实现根据邮箱查询用户信息的逻辑
CustomUser user = userRepository.findByEmail(email);
if (user == null) {
throw new UsernameNotFoundException("User not found with email: " + email);
}
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
return new CustomUserDetails(user.getEmail(), user.isEnabled(), authorities);
}
}
Step 6: 配置Spring Security
在Spring Security配置类中配置自定义过滤器、认证提供者和UserDetailsService。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final EmailVerificationAuthenticationProvider emailVerificationAuthenticationProvider;
private final CustomUserDetailsService customUserDetailsService;
@Autowired
public SecurityConfig(EmailVerificationAuthenticationProvider emailVerificationAuthenticationProvider, CustomUserDetailsService customUserDetailsService) {
this.emailVerificationAuthenticationProvider = emailVerificationAuthenticationProvider;
this.customUserDetailsService = customUserDetailsService;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(emailVerificationAuthenticationProvider);
auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(emailVerificationAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login");
}
@Bean
public EmailVerificationAuthenticationFilter emailVerificationAuthenticationFilter() throws Exception {
EmailVerificationAuthenticationFilter filter = new EmailVerificationAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance(); // For simplicity, use NoOpPasswordEncoder
}
}