spring security 短信验证码

本文详细介绍了如何在Spring Security框架中实现短信验证码的功能,包括自定义认证Token、短信验证码检验及拦截器设置,同时提供了完整的代码示例和前端页面展示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


spring security 短信验证码

 

 

*************************

示例

 

***************

config 层

 

MessageCodeAuthenticationToken:自定义认证token

public class MessageCodeAuthenticationToken extends AbstractAuthenticationToken {

    private static final long serialVersionUID = 500L;

    private final Object principal;

    public MessageCodeAuthenticationToken(Object principal) {
        super(null);
        this.principal = principal;
        this.setAuthenticated(false);
    }

    public MessageCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        super.setAuthenticated(true);
    }

    @Override
    public Object getCredentials() {
        return null;
    }

    public Object getPrincipal() { 
        return this.principal;
    }

    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        if (isAuthenticated) {
            throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        } else {
            super.setAuthenticated(false);
        }
    }

    public void eraseCredentials() {
        super.eraseCredentials();
    }
}

 

MessageCodeAuthenticationProvider:检验短信验证码,构造token

@Component
public class MessageCodeAuthenticationProvider implements AuthenticationProvider {

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        MessageCodeAuthenticationToken authenticationToken=(MessageCodeAuthenticationToken)authentication;

        String phone=authenticationToken.getPrincipal().toString();
        System.out.println(phone);

        checkMessageCode(phone);

        System.out.println("认证成功");
        MessageCodeAuthenticationToken authenticationResult=new MessageCodeAuthenticationToken(phone,
                Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));
        authenticationResult.setDetails(authenticationToken.getDetails());

        return authenticationResult;
    }

    private void checkMessageCode(String phone){
        HttpServletRequest request=((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String request_code=request.getParameter("code");

        Map<String,String> map=(Map<String, String>) request.getSession().getAttribute("message_code");
        if (map==null){
            throw new BadCredentialsException("未申请验证码");
        }

        String session_phone= map.get("phone");
        String session_code= map.get("code");

        if (!phone.equals(session_phone)){
            throw new BadCredentialsException("申请验证的手机号不一致");
        }

        if (!request_code.equals(session_code)){
            throw new BadCredentialsException("验证码不一致");
        }
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return MessageCodeAuthenticationToken.class.isAssignableFrom(aClass);
    }
}

 

MessageCodeAuthenticationFilter:短信验证码拦截器,拦截路径:/message/login

public class MessageCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    private String MessageCodeParameter="phone";

    private boolean postOnly = true;

    public MessageCodeAuthenticationFilter(){
        super(new AntPathRequestMatcher("/message/login", "POST"));
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
        if (this.postOnly && !httpServletRequest.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + httpServletRequest.getMethod());
        }else {
            String phone=httpServletRequest.getParameter(MessageCodeParameter);

            if (phone==null){
                phone="";
            }

            phone=phone.trim();
            MessageCodeAuthenticationToken authRequest=new MessageCodeAuthenticationToken(phone);
            this.setDetails(httpServletRequest,authRequest);

            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }

    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }

    protected void setDetails(HttpServletRequest request, MessageCodeAuthenticationToken authRequest) {
        authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }

    public void setMessageCodeParameter(String messageCodeParameter){
        Assert.hasText(messageCodeParameter, "MessageCode parameter must not be empty or null");
        this.MessageCodeParameter=messageCodeParameter;
    }

    public String getMessageCodeParameter() {
        return MessageCodeParameter;
    }
}

 

MessageCodeAuthenticationFailureHandler:短信认证失败相关处理

@Component
public class MessageCodeAuthenticationFailureHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.setContentType("application/json;charset=UTF-8");
        httpServletResponse.getWriter().write(e.getMessage());
    }
}

 

 

WebConfig

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Resource
    private MessageCodeFilter messageCodeFilter;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(messageCodeFilter).addPathPatterns("/message/code");
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login/form").setViewName("login");
    }
}

 

 

MessageCodeSecurityConfig:短信安全相关配置

@Configuration
public class MessageCodeSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

    @Resource
    private MessageCodeAuthenticationProvider messageCodeAuthenticationProvider;

    @Resource
    private MessageCodeAuthenticationFailureHandler messageCodeAuthenticationFailureHandler;

    @Override
    public void configure(HttpSecurity builder) throws Exception {
        MessageCodeAuthenticationFilter messageCodeAuthenticationFilter=new MessageCodeAuthenticationFilter();
        messageCodeAuthenticationFilter.setAuthenticationManager(builder.getSharedObject(AuthenticationManager.class));
        messageCodeAuthenticationFilter.setAuthenticationFailureHandler(messageCodeAuthenticationFailureHandler);

        builder.authenticationProvider(messageCodeAuthenticationProvider)
                .addFilterAfter(messageCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

 

 

WebSecurityConfig

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private MessageCodeSecurityConfig messageCodeSecurityConfig;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin().loginPage("/login/form").and().authorizeRequests()
                .antMatchers("/hello").hasAuthority("ROLE_USER")
                .antMatchers("/**").permitAll();

        http.apply(messageCodeSecurityConfig);
    }
}

 

***************

filter 层

 

MessageCodeFilter:拦截路径 /message/code,检验手机号是否存在

@Component
public class MessageCodeFilter implements HandlerInterceptor {

    private List<String> messageList= Arrays.asList("123456","654321");

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String message=request.getParameter("phone");

        if (!messageList.contains(message)){
            response.getWriter().write("手机号不存在");

            return false;
        }else {
            return true;
        }
    }
}

 

 

***************

controller 层

 

HelloController

@RestController
public class HelloController {

    @RequestMapping("/hello")
    public String hello(Principal principal){
        return "hello "+principal.getName();
    }

    @RequestMapping("/message/code")
    public Map<String,String> messageCode(HttpServletRequest request){
        String phone=request.getParameter("phone");
        String code=generateCode();
        System.out.println("code:"+code);

        Map<String,String> map=new HashMap<>();
        map.put("phone",phone);
        map.put("code",code);

        request.getSession().setAttribute("message_code",map);

        return map;
    }

    private String generateCode(){
        StringBuilder buffer=new StringBuilder();
        Random r=new Random();

        for (int i=0;i<6;i++){
            buffer.append(r.nextInt(10));
        }

        return buffer.toString();
    }
}

 

 

***************

前端页面

 

login.html

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/js/jquery-3.5.1.min.js"></script>
    <script>
        $(function(){
            $("#get_code").click(function () {
                $.get({
                    url: "/message/code?phone="+$("#phone").val(),
                    success: function (result) {
                        $("#result_code").html(result.code)
                    }
                })
            })
        })
    </script>
</head>
<body>
<form th:action="@{/message/login}" th:align="center" method="post">
    phone:<input type="text" id="phone" name="phone"><br>
    code:<input type="text" id="code" name="code">
    <a th:id="get_code">获取短信验证码</a><br>
    <button th:id="code_login">code</button>
</form>

<div th:id="result_code" align="center"></div>

</body>
</html>

 

 

*************************

使用测试

 

localhost:8080/hello

                        

                       

认证通过后,输出:hello 123456

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值