spring-security(三):自定义验证实现图片验证码功能

前言

验证码相关的依赖和代码,在上一篇已经写到了,这里不在赘述。

1.实现一个封装额外信息的detail类

package com.demo.springsecuritydemo.detail;

import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * 继承自webAuthenticationDetail
 *
 * @author suvue
 * @date 2020/5/3
 */
public class MyWebAuthenticationDetail extends WebAuthenticationDetails {
    public String imageCode;
    public String savedImageCode;

    public String getImageCode() {
        return imageCode;
    }

    public void setImageCode(String imageCode) {
        this.imageCode = imageCode;
    }

    public String getSavedImageCode() {
        return savedImageCode;
    }

    public void setSavedImageCode(String savedImageCode) {
        this.savedImageCode = savedImageCode;
    }

    /**
     * Records the remote address and will also set the session Id if a session already
     * exists (it won't create one).
     *
     * @param request that the authentication request was received from
     */
    public MyWebAuthenticationDetail(HttpServletRequest request) {
        super(request);
        //补充用户提交的验证码和session中保存的验证码
        this.imageCode = request.getParameter("captcha");
        final HttpSession session = request.getSession();
        this.savedImageCode= (String) session.getAttribute("captcha");
        if (!StringUtils.isEmpty(this.savedImageCode)){
            //清除验证码,不管失败成功,客户端应在登录失败时刷新验证码
            session.removeAttribute("captcha");
        }
    }
}

2.把它提供给一个自定义的AuthenticationDetailsSource

package com.demo.springsecuritydemo.detailSource;

import com.demo.springsecuritydemo.detail.MyWebAuthenticationDetail;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component
public class MyWebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest,
        WebAuthenticationDetails> {
    @Override
    public WebAuthenticationDetails buildDetails(HttpServletRequest request) {
        return new MyWebAuthenticationDetail(request);
    }
}

3.接下来实现我们自定义的AuthenticationProvider

package com.demo.springsecuritydemo.provider;

import com.demo.springsecuritydemo.detail.MyWebAuthenticationDetail;
import com.demo.springsecuritydemo.exception.VerificationCodeException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Objects;

/**
 * 实现图片验证码的AuthenticationProvider
 *
 * @author suvue
 * @date 2020/5/3
 */
@Component
public class MyAuthenticationProvider extends DaoAuthenticationProvider {

    public MyAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder){
        this.setUserDetailsService(userDetailsService);
        this.setPasswordEncoder(passwordEncoder);
    }

    /**
     * 附加认证过程
     */
    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        //实现图片验证码的校验逻辑
        final MyWebAuthenticationDetail details = (MyWebAuthenticationDetail) authentication.getDetails();
        final String imageCode = details.getImageCode();
        final String savedImageCode = details.getSavedImageCode();
        //校验不通过,抛出异常
        if (StringUtils.isEmpty(imageCode) || StringUtils.isEmpty(savedImageCode) || !Objects.equals(imageCode,
                savedImageCode)) {
            throw new VerificationCodeException();
        }
        //调用父类完成密码验证
        super.additionalAuthenticationChecks(userDetails, authentication);
    }
}

4.最后在WebSecurityConfig中完成相关配置

这里贴出完整代码,关键点都有相关注释的

package com.demo.springsecuritydemo.config;

import com.demo.springsecuritydemo.filters.VerificationCodeFilter;
import com.demo.springsecuritydemo.handler.MyAuthenticationFailureHandler;
import com.demo.springsecuritydemo.service.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetails;

import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;

/**
 * 自定义验证完善图片验证码
 *
 * @author suvue
 * @date 2020/5/3
 */
@EnableWebSecurity
public class WebSecurityConfig1 extends WebSecurityConfigurerAdapter {
    @Autowired
    private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> myWebAuthenticationDetailsSource;

    @Autowired
    private AuthenticationProvider authenticationProvider;

    @Autowired
    private MyUserDetailsService myUserDetailsService;

    @Autowired
    PasswordEncoder passwordEncoder;


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/api/**").hasRole("ADMIN")
                .antMatchers("/user/api/**").hasRole("USER")
                //开放captcha.jpg的访问权限
                .antMatchers("/app/api/**","/captcha.jpg").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                //把我们写的detailsSource传给控制校验的filter
                .authenticationDetailsSource(myWebAuthenticationDetailsSource)
                .loginPage("/myLogin.html")
                .loginProcessingUrl("/auth/form")
                .successHandler((httpServletRequest, httpServletResponse, authentication) -> {
                    httpServletResponse.setContentType("application/json;charset=UTF-8");
                    final PrintWriter out = httpServletResponse.getWriter();
                    out.write("{\"error_code\":\"0\",\"message\":\"欢迎登录系统!\"}");
                })
                .failureHandler(new MyAuthenticationFailureHandler())
                .permitAll()
                .and()
                .csrf().disable();

        //将过滤器添加再UsernamePasswordAuthenticationFilter之前
        http.addFilterBefore(new VerificationCodeFilter(), UsernamePasswordAuthenticationFilter.class);
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //使用我们自定义的数据库模型
        auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder);
        //添加我们的校验流程
        auth.authenticationProvider(authenticationProvider);
    }
}

你可能感兴趣的:(spring-security)