SpringSecurity同时整合短信登录验证和邮箱登录验证

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();
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这是一个很常见的问题。首先,你需要在Spring Boot项目中添加依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> ``` 然后,你需要创建一个`JwtTokenFilter`类,用于在每个请求中验证JWT令牌: ```java public class JwtTokenFilter extends OncePerRequestFilter { private final JwtTokenProvider tokenProvider; public JwtTokenFilter(JwtTokenProvider tokenProvider) { this.tokenProvider = tokenProvider; } @Override protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { String token = tokenProvider.resolveToken(httpServletRequest); try { if (token != null && tokenProvider.validateToken(token)) { Authentication auth = tokenProvider.getAuthentication(token); SecurityContextHolder.getContext().setAuthentication(auth); } } catch (JwtException e) { SecurityContextHolder.clearContext(); httpServletResponse.sendError(HttpStatus.BAD_REQUEST.value(), e.getMessage()); return; } filterChain.doFilter(httpServletRequest, httpServletResponse); } } ``` 接下来,你需要创建一个`JwtTokenProvider`类,用于创建和验证JWT令牌: ```java @Component public class JwtTokenProvider { private static final String SECRET_KEY = "secret-key"; private static final long EXPIRATION_TIME = 864_000_000; // 10 days private final UserDetailsService userDetailsService; public JwtTokenProvider(UserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } public String createToken(Authentication authentication) { UserDetailsImpl user = (UserDetailsImpl) authentication.getPrincipal(); Date now = new Date(); Date expiryDate = new Date(now.getTime() + EXPIRATION_TIME); return Jwts.builder() .setSubject(user.getUsername()) .setIssuedAt(now) .setExpiration(expiryDate) .signWith(SignatureAlgorithm.HS512, SECRET_KEY) .compact(); } public Authentication getAuthentication(String token) { UserDetails userDetails = userDetailsService.loadUserByUsername(getUsername(token)); return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); } public String getUsername(String token) { return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getSubject(); } public boolean validateToken(String token) { try { Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token); return true; } catch (JwtException | IllegalArgumentException e) { throw new JwtAuthenticationException("Expired or invalid JWT token"); } } public String resolveToken(HttpServletRequest request) { String bearerToken = request.getHeader("Authorization"); if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7); } return null; } } ``` 最后,你需要修改`WebSecurityConfig`类,以使用JWT令牌进行身份验证: ```java @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final JwtTokenProvider tokenProvider; public WebSecurityConfig(JwtTokenProvider tokenProvider) { this.tokenProvider = tokenProvider; } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/api/auth/**").permitAll() .anyRequest().authenticated() .and() .apply(new JwtConfigurer(tokenProvider)); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(12); } } ``` 以上就是整合Spring Security和JWT进行身份验证的基本步骤。当然,具体实现还需要根据你的业务需求进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值