springboot整合spring-Security實現驗證碼登錄


參考地址:https://www.jianshu.com/p/9d08c767b33e

springboot整合spring-security實現簡單的登錄注銷 的基礎上進行開發。

1、添加生成驗證碼的控制器。

(1)、生成驗證碼

 1     /**
 2      * 引入 Security 配置屬性類
 3      */
 4     @Autowired
 5     private SecurityProperties securityProperties;
 6 
 7 
 8     @Override
 9     public ImageCode createCode(HttpServletRequest request ) {
10         //如果請求中有 width 參數,則用請求中的,否則用 配置屬性中的
11         int width = ServletRequestUtils.getIntParameter(request,"width",securityProperties.getWidth());
12         //高度(寬度)
13         int height = ServletRequestUtils.getIntParameter(request,"height",securityProperties.getHeight());
14         //圖片驗證碼字符個數
15         int length = securityProperties.getLength();
16         //過期時間
17         int expireIn = securityProperties.getExpireIn();
18 
19         BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
20 
21         Graphics g = image.getGraphics();
22 
23         Random random = new Random();
24 
25         g.setColor(getRandColor(200, 250));
26         g.fillRect(0, 0, width, height);
27         g.setFont(new Font("Times New Roman", Font.ITALIC, 20));
28         g.setColor(getRandColor(160, 200));
29         for (int i = 0; i < 155; i++) {
30             int x = random.nextInt(width);
31             int y = random.nextInt(height);
32             int xl = random.nextInt(12);
33             int yl = random.nextInt(12);
34             g.drawLine(x, y, x + xl, y + yl);
35         }
36 
37         String sRand = "";
38         for (int i = 0; i < length; i++) {
39             String rand = String.valueOf(random.nextInt(10));
40             sRand += rand;
41             g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
42             g.drawString(rand, 13 * i + 6, 16);
43         }
44 
45         g.dispose();
46 
47         return new ImageCode(image, sRand, expireIn);
48     }
49 
50     /**
51      * 生成隨機背景條紋
52      */
53     private Color getRandColor(int fc, int bc) {
54         Random random = new Random();
55         if (fc > 255) {
56             fc = 255;
57         }
58         if (bc > 255) {
59             bc = 255;
60         }
61         int r = fc + random.nextInt(bc - fc);
62         int g = fc + random.nextInt(bc - fc);
63         int b = fc + random.nextInt(bc - fc);
64         return new Color(r, g, b);
65     }
View Code

(2)、驗證碼控制器

 public static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";

    @Autowired
    private ValidateCodeGenerator imageCodeGenerator;

    /**
     * Session 對象
     */
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

    @GetMapping("/code/image")
    public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
        ImageCode imageCode = imageCodeGenerator.createCode(request);
        //將隨機數 放到Session中
        sessionStrategy.setAttribute(new ServletWebRequest(request),SESSION_KEY,imageCode);
        request.getSession().setAttribute(SESSION_KEY,imageCode);
        //寫給response 響應
        response.setHeader("Cache-Control", "no-store, no-cache");
        response.setContentType("image/jpeg");
        ImageIO.write(imageCode.getImage(),"JPEG",response.getOutputStream());
    }

(3)、其它輔助類

 1 @Data
 2 public class ImageCode {
 3 
 4     /**
 5      * 圖片
 6      */
 7     private BufferedImage image;
 8     /**
 9      * 隨機數
10      */
11     private String code;
12     /**
13      * 過期時間
14      */
15     private LocalDateTime expireTime;
16 
17     public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {
18         this.image = image;
19         this.code = code;
20         this.expireTime = expireTime;
21     }
22     public ImageCode(BufferedImage image, String code, int  expireIn) {
23         this.image = image;
24         this.code = code;
25         //當前時間  加上  設置過期的時間
26         this.expireTime = LocalDateTime.now().plusSeconds(expireIn);
27     }
28 
29     public boolean isExpried(){
30         //如果 過期時間 在 當前日期 之前,則驗證碼過期
31         return LocalDateTime.now().isAfter(expireTime);
32     }
33 }
View Code
 1 @ConfigurationProperties(prefix = "sso.security.code.image")
 2 @Component
 3 @Data
 4 public class SecurityProperties {
 5 
 6     /**
 7      * 驗證碼寬度
 8      */
 9     private int width = 67;
10     /**
11      * 高度
12      */
13     private int height = 23;
14     /**
15      * 長度(幾個數字)
16      */
17     private int length = 4;
18     /**
19      * 過期時間
20      */
21     private int expireIn = 60;
22 
23     /**
24      * 需要圖形驗證碼的 url
25      */
26     private String url;
27 }
View Code

(4)、驗證

2、添加過濾器,進行驗證碼驗證

 1 @Component
 2 @Slf4j
 3 public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {
 4 
 5     /**
 6      * 登錄失敗處理器
 7      */
 8     @Autowired
 9     private AuthenticationFailureHandler failureHandler;
10     /**
11      * Session 對象
12      */
13     private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
14 
15     /**
16      * 創建一個Set 集合 存放 需要驗證碼的 urls
17      */
18     private Set<String> urls = new HashSet<>();
19     /**
20      * spring的一個工具類:用來判斷 兩字符串 是否匹配
21      */
22     private AntPathMatcher pathMatcher = new AntPathMatcher();
23 
24     @Autowired
25     private SecurityProperties securityProperties;
26     /**
27      * 這個方法是 InitializingBean 接口下的一個方法, 在初始化配置完成后 運行此方法
28      */
29     @Override
30     public void afterPropertiesSet() throws ServletException {
31         super.afterPropertiesSet();
32         //將 application 配置中的 url 屬性進行 切割
33         String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getUrl(), ",");
34         //添加到 Set 集合里
35         urls.addAll(Arrays.asList(configUrls));
36         //因為登錄請求一定要有驗證碼 ,所以直接 add 到set 集合中
37         urls.add("/authentication/form");
38     }
39 
40     @Override
41     protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
42 
43         boolean action = false;
44         for (String url:urls){
45             //如果請求的url 和 配置中的url 相匹配
46             if (pathMatcher.match(url,httpServletRequest.getRequestURI())){
47                 action = true;
48             }
49         }
50 
51         //攔截請求
52         if (action){
53             logger.info("攔截成功"+httpServletRequest.getRequestURI());
54             //如果是登錄請求
55             try {
56                 validate(new ServletWebRequest(httpServletRequest));
57             }catch (ValidateCodeException exception){
58                 //返回錯誤信息給 失敗處理器
59                 failureHandler.onAuthenticationFailure(httpServletRequest,httpServletResponse,exception);
60                 return;
61             }
62 
63         }
64         filterChain.doFilter(httpServletRequest,httpServletResponse);
65 
66     }
67     private void validate(ServletWebRequest request) throws ServletRequestBindingException {
68         //從session中取出 驗證碼
69         ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request,ValidateCodeController.SESSION_KEY);
70         //從request 請求中 取出 驗證碼
71         String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),"imageCode");
72 
73         if (StringUtils.isBlank(codeInRequest)){
74             logger.info("驗證碼不能為空");
75             throw new ValidateCodeException("驗證碼不能為空");
76         }
77         if (codeInSession == null){
78             logger.info("驗證碼不存在");
79             throw new ValidateCodeException("驗證碼不存在");
80         }
81         if (codeInSession.isExpried()){
82             logger.info("驗證碼已過期");
83             sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY);
84             throw new ValidateCodeException("驗證碼已過期");
85         }
86         if (!StringUtils.equals(codeInSession.getCode(),codeInRequest)){
87             logger.info("驗證碼不匹配"+"codeInSession:"+codeInSession.getCode() +", codeInRequest:"+codeInRequest);
88             throw new ValidateCodeException("驗證碼不匹配");
89         }
90         //把對應 的 session信息  刪掉
91         sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY);
92     }
View Code

3、在核心配置BrowserSecurityConfig中添加過濾器配置

 1  @Autowired
 2     private ValidateCodeFilter validateCodeFilter;
 3 
 4     @Override
 5     protected void configure(HttpSecurity http) throws Exception {
 6         //在UsernamePasswordAuthenticationFilter 過濾器前 加一個過濾器 來搞驗證碼
 7         http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
 8                 //表單登錄 方式
 9                 .formLogin()
10                 .loginPage("/authentication/require")
11                 //登錄需要經過的url請求
12                 .loginProcessingUrl("/authentication/form")
13                 .passwordParameter("pwd")
14                 .usernameParameter("user")
15                 .successHandler(mySuccessHandler)
16                 .failureHandler(myFailHandler)
17                 .and()
18                 //請求授權
19                 .authorizeRequests()
20                 //不需要權限認證的url
21                 .antMatchers("/authentication/*","/code/image").permitAll()
22                 //任何請求
23                 .anyRequest()
24                 //需要身份認證
25                 .authenticated()
26                 .and()
27                 //關閉跨站請求防護
28                 .csrf().disable();
29         //默認注銷地址:/logout
30         http.logout().
31                 //注銷之后 跳轉的頁面
32                 logoutSuccessUrl("/authentication/require");
33     }
View Code

4、異常輔助類

public class ValidateCodeException extends AuthenticationException {
    public ValidateCodeException(String msg, Throwable t) {
        super(msg, t);
    }

    public ValidateCodeException(String msg) {
        super(msg);
    }
}

5、測試

(1)、不輸入驗證碼

 (2)、添加驗證碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM