您好,登錄后才能下訂單哦!
這篇文章主要介紹了Spring Security實現驗證碼登錄功能,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
在spring security實現登錄注銷功能的基礎上進行開發。
1、添加生成驗證碼的控制器。
(1)、生成驗證碼
/** * 引入 Security 配置屬性類 */ @Autowired private SecurityProperties securityProperties; @Override public ImageCode createCode(HttpServletRequest request ) { //如果請求中有 width 參數,則用請求中的,否則用 配置屬性中的 int width = ServletRequestUtils.getIntParameter(request,"width",securityProperties.getWidth()); //高度(寬度) int height = ServletRequestUtils.getIntParameter(request,"height",securityProperties.getHeight()); //圖片驗證碼字符個數 int length = securityProperties.getLength(); //過期時間 int expireIn = securityProperties.getExpireIn(); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); Random random = new Random(); g.setColor(getRandColor(200, 250)); g.fillRect(0, 0, width, height); g.setFont(new Font("Times New Roman", Font.ITALIC, 20)); g.setColor(getRandColor(160, 200)); for (int i = 0; i < 155; i++) { int x = random.nextInt(width); int y = random.nextInt(height); int xl = random.nextInt(12); int yl = random.nextInt(12); g.drawLine(x, y, x + xl, y + yl); } String sRand = ""; for (int i = 0; i < length; i++) { String rand = String.valueOf(random.nextInt(10)); sRand += rand; g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110))); g.drawString(rand, 13 * i + 6, 16); } g.dispose(); return new ImageCode(image, sRand, expireIn); } /** * 生成隨機背景條紋 */ private Color getRandColor(int fc, int bc) { Random random = new Random(); if (fc > 255) { fc = 255; } if (bc > 255) { bc = 255; } int r = fc + random.nextInt(bc - fc); int g = fc + random.nextInt(bc - fc); int b = fc + random.nextInt(bc - fc); return new Color(r, g, b); }
(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)、其它輔助類
@Data public class ImageCode { /** * 圖片 */ private BufferedImage image; /** * 隨機數 */ private String code; /** * 過期時間 */ private LocalDateTime expireTime; public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) { this.image = image; this.code = code; this.expireTime = expireTime; } public ImageCode(BufferedImage image, String code, int expireIn) { this.image = image; this.code = code; //當前時間 加上 設置過期的時間 this.expireTime = LocalDateTime.now().plusSeconds(expireIn); } public boolean isExpried(){ //如果 過期時間 在 當前日期 之前,則驗證碼過期 return LocalDateTime.now().isAfter(expireTime); } }
@ConfigurationProperties(prefix = "sso.security.code.image") @Component @Data public class SecurityProperties { /** * 驗證碼寬度 */ private int width = 67; /** * 高度 */ private int height = 23; /** * 長度(幾個數字) */ private int length = 4; /** * 過期時間 */ private int expireIn = 60; /** * 需要圖形驗證碼的 url */ private String url; }
(4)、驗證
2、添加過濾器,進行驗證碼驗證
@Component @Slf4j public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean { /** * 登錄失敗處理器 */ @Autowired private AuthenticationFailureHandler failureHandler; /** * Session 對象 */ private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); /** * 創建一個Set 集合 存放 需要驗證碼的 urls */ private Set<String> urls = new HashSet<>(); /** * spring的一個工具類:用來判斷 兩字符串 是否匹配 */ private AntPathMatcher pathMatcher = new AntPathMatcher(); @Autowired private SecurityProperties securityProperties; /** * 這個方法是 InitializingBean 接口下的一個方法, 在初始化配置完成后 運行此方法 */ @Override public void afterPropertiesSet() throws ServletException { super.afterPropertiesSet(); //將 application 配置中的 url 屬性進行 切割 String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getUrl(), ","); //添加到 Set 集合里 urls.addAll(Arrays.asList(configUrls)); //因為登錄請求一定要有驗證碼 ,所以直接 add 到set 集合中 urls.add("/authentication/form"); } @Override protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { boolean action = false; for (String url:urls){ //如果請求的url 和 配置中的url 相匹配 if (pathMatcher.match(url,httpServletRequest.getRequestURI())){ action = true; } } //攔截請求 if (action){ logger.info("攔截成功"+httpServletRequest.getRequestURI()); //如果是登錄請求 try { validate(new ServletWebRequest(httpServletRequest)); }catch (ValidateCodeException exception){ //返回錯誤信息給 失敗處理器 failureHandler.onAuthenticationFailure(httpServletRequest,httpServletResponse,exception); return; } } filterChain.doFilter(httpServletRequest,httpServletResponse); } private void validate(ServletWebRequest request) throws ServletRequestBindingException { //從session中取出 驗證碼 ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request,ValidateCodeController.SESSION_KEY); //從request 請求中 取出 驗證碼 String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),"imageCode"); if (StringUtils.isBlank(codeInRequest)){ logger.info("驗證碼不能為空"); throw new ValidateCodeException("驗證碼不能為空"); } if (codeInSession == null){ logger.info("驗證碼不存在"); throw new ValidateCodeException("驗證碼不存在"); } if (codeInSession.isExpried()){ logger.info("驗證碼已過期"); sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY); throw new ValidateCodeException("驗證碼已過期"); } if (!StringUtils.equals(codeInSession.getCode(),codeInRequest)){ logger.info("驗證碼不匹配"+"codeInSession:"+codeInSession.getCode() +", codeInRequest:"+codeInRequest); throw new ValidateCodeException("驗證碼不匹配"); } //把對應 的 session信息 刪掉 sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY); }
3、在核心配置BrowserSecurityConfig中添加過濾器配置
@Autowired private ValidateCodeFilter validateCodeFilter; @Override protected void configure(HttpSecurity http) throws Exception { //在UsernamePasswordAuthenticationFilter 過濾器前 加一個過濾器 來搞驗證碼 http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) //表單登錄 方式 .formLogin() .loginPage("/authentication/require") //登錄需要經過的url請求 .loginProcessingUrl("/authentication/form") .passwordParameter("pwd") .usernameParameter("user") .successHandler(mySuccessHandler) .failureHandler(myFailHandler) .and() //請求授權 .authorizeRequests() //不需要權限認證的url .antMatchers("/authentication/*","/code/image").permitAll() //任何請求 .anyRequest() //需要身份認證 .authenticated() .and() //關閉跨站請求防護 .csrf().disable(); //默認注銷地址:/logout http.logout(). //注銷之后 跳轉的頁面 logoutSuccessUrl("/authentication/require"); }
4、異常輔助類
public class ValidateCodeException extends AuthenticationException { public ValidateCodeException(String msg, Throwable t) { super(msg, t); } public ValidateCodeException(String msg) { super(msg); } }
5、測試
(1)、不輸入驗證碼
(2)、添加驗證碼
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。