日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

SpringBoot 整合Security——自定义表单登录

發布時間:2025/1/21 javascript 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringBoot 整合Security——自定义表单登录 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 一、添加驗證碼
      • 1.1 驗證servlet
      • 1.2 修改 login.html
      • 1.3 添加匿名訪問 Url
    • 二、AJAX 驗證
    • 三、過濾器驗證
      • 3.1 編寫驗證碼過濾器
      • 3.2 注入過濾器
      • 3.3 運行程序
    • 四、Spring Security 驗證
      • 4.1 WebAuthenticationDetails
      • 4.2 AuthenticationDetailsSource
      • 4.3 AuthenticationProvider
      • 4.4 運行程序

通過前面三篇文章,你應該大致了解了 Spring Security 的流程。你應該發現了,真正的 login 請求是由 Spring Security 幫我們處理的,那么我們如何實現自定義表單登錄呢,比如添加一個驗證碼…

一、添加驗證碼

1.1 驗證servlet

public class VerifyServlet extends HttpServlet {/*** 驗證碼圖片的寬度。*/private int width = 100;/*** 驗證碼圖片的高度。*/private int height = 30;/*** 驗證碼字符個數*/private int codeCount = 4;/*** 字體高度*/private int fontHeight;/*** 干擾線數量*/private int interLine = 16;/*** 第一個字符的x軸值,因為后面的字符坐標依次遞增,所以它們的x軸值是codeX的倍數*/private int codeX;/*** codeY ,驗證字符的y軸值,因為并行所以值一樣*/private int codeY;/*** codeSequence 表示字符允許出現的序列值*/char[] codeSequence = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J','K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W','X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };/*** 初始化驗證圖片屬性*/@Overridepublic void init() throws ServletException {// 從web.xml中獲取初始信息// 寬度String strWidth = this.getInitParameter("width");// 高度String strHeight = this.getInitParameter("height");// 字符個數String strCodeCount = this.getInitParameter("codeCount");// 將配置的信息轉換成數值try {if (strWidth != null && strWidth.length() != 0) {width = Integer.parseInt(strWidth);}if (strHeight != null && strHeight.length() != 0) {height = Integer.parseInt(strHeight);}if (strCodeCount != null && strCodeCount.length() != 0) {codeCount = Integer.parseInt(strCodeCount);}} catch (NumberFormatException e) {e.printStackTrace();}//width-4 除去左右多余的位置,使驗證碼更加集中顯示,減得越多越集中。//codeCount+1 //等比分配顯示的寬度,包括左右兩邊的空格codeX = (width-4) / (codeCount+1);//height - 10 集中顯示驗證碼fontHeight = height - 10;codeY = height - 7;}/*** @param request* @param response* @throws ServletException* @throws java.io.IOException*/@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException {// 定義圖像bufferBufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);Graphics2D gd = buffImg.createGraphics();// 創建一個隨機數生成器類Random random = new Random();// 將圖像填充為白色gd.setColor(Color.LIGHT_GRAY);gd.fillRect(0, 0, width, height);// 創建字體,字體的大小應該根據圖片的高度來定。Font font = new Font("Times New Roman", Font.PLAIN, fontHeight);// 設置字體。gd.setFont(font);// 畫邊框。gd.setColor(Color.BLACK);gd.drawRect(0, 0, width - 1, height - 1);// 隨機產生16條干擾線,使圖象中的認證碼不易被其它程序探測到。gd.setColor(Color.gray);for (int i = 0; i < interLine; i++) {int x = random.nextInt(width);int y = random.nextInt(height);int xl = random.nextInt(12);int yl = random.nextInt(12);gd.drawLine(x, y, x + xl, y + yl);}// randomCode用于保存隨機產生的驗證碼,以便用戶登錄后進行驗證。StringBuffer randomCode = new StringBuffer();int red = 0, green = 0, blue = 0;// 隨機產生codeCount數字的驗證碼。for (int i = 0; i < codeCount; i++) {// 得到隨機產生的驗證碼數字。String strRand = String.valueOf(codeSequence[random.nextInt(36)]);// 產生隨機的顏色分量來構造顏色值,這樣輸出的每位數字的顏色值都將不同。red = random.nextInt(255);green = random.nextInt(255);blue = random.nextInt(255);// 用隨機產生的顏色將驗證碼繪制到圖像中。gd.setColor(new Color(red,green,blue));gd.drawString(strRand, (i + 1) * codeX, codeY);// 將產生的四個隨機數組合在一起。randomCode.append(strRand);}// 將四位數字的驗證碼保存到Session中。HttpSession session = request.getSession();session.setAttribute("validateCode", randomCode.toString());// 禁止圖像緩存。response.setHeader("Pragma", "no-cache");response.setHeader("Cache-Control", "no-cache");response.setDateHeader("Expires", 0);response.setContentType("image/jpeg");// 將圖像輸出到Servlet輸出流中。ServletOutputStream sos = response.getOutputStream();ImageIO.write(buffImg, "jpeg", sos);sos.close();}}

然后在 Application 中注入該 Servlet:

@SpringBootApplication public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}@Beanpublic ServletRegistrationBean servletRegistrationBean() {ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new VerifyServlet());servletRegistrationBean.addUrlMappings("/getVerifyCode");return servletRegistrationBean;} }

1.2 修改 login.html

在原本的 Login 頁面基礎上加上驗證碼字段

<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>登陸</title> </head> <body> <h1>登陸</h1> <form method="post" action="/login"><div>用戶名:<input type="text" name="username"></div><div>密碼:<input type="password" name="password"></div><div><input type="text" class="form-control" name="verifyCode" required="required" placeholder="驗證碼" /><img src="getVerifyCode" title="看不清,請點我" onclick="refresh(this)" onmouseover="mouseover(this)" /></div><div><label><input type="checkbox" name="remember-me"/>自動登錄</label><button type="submit">立即登陸</button></div> </form> <script>function refresh(obj) { obj.src = "getVerifyCode?" + Math.random(); }function mouseover(obj) { obj.style.cursor = "pointer"; } </script> </body> </html>

1.3 添加匿名訪問 Url

在 WebSecurityConfig 中允許該 Url 的匿名訪問,不然沒有登錄是沒有辦法訪問的:

@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests()// 如果有允許匿名的url,填在下面.antMatchers("/getVerifyCode").permitAll().anyRequest().authenticated().and().formLogin().loginPage("/login")// 設置登陸成功頁.defaultSuccessUrl("/").permitAll().failureHandler(new SimpleUrlAuthenticationFailureHandler())//登錄失敗url.failureForwardUrl("/login/error").authenticationDetailsSource(authenticationDetailsSource).and()// 自定義登陸用戶名和密碼參數,默認為username和password// .usernameParameter("username")// .passwordParameter("password").logout().permitAll()//基于內存自動登錄.and().rememberMe().tokenRepository(persistentTokenRepository()).tokenValiditySeconds(60) ;http.csrf().disable();}

這樣驗證碼就加好了,運行下程序:


下面才算是這篇文章真正的部分。我們如何才能實現驗證碼驗證呢,思考一下,應該有以下幾種實現方式:

  • 登錄表單提交前發送 AJAX 驗證驗證碼
  • 使用自定義過濾器(Filter),在 Spring security 校驗前驗證驗證碼合法性
  • 和用戶名、密碼一起發送到后臺,在 Spring security 中進行驗證
  • 二、AJAX 驗證

    使用 AJAX 方式驗證和我們 Spring Security 框架就沒有任何關系了,其實就是表單提交前先發個 HTTP 請求驗證驗證碼,本篇不再贅述。

    三、過濾器驗證

    使用過濾器的思路是:在 Spring Security 處理登錄驗證請求前,驗證驗證碼,如果正確,放行;如果不正確,調到異常。

    3.1 編寫驗證碼過濾器

    自定義一個過濾器,實現 OncePerRequestFilter (該 Filter 保證每次請求一定會過濾),在 isProtectedUrl() 方法中攔截了 POST 方式的 /login 請求。

    在邏輯處理中從 request 中取出驗證碼,并進行驗證,如果驗證成功,放行;驗證失敗,手動生成異常。
    //獲取當前線程綁定的request對象
    HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    String validateCode = ((String)request.getSession().getAttribute(“validateCode”)).toUpperCase();

    /*** 基于filter做的驗證碼* 該 Filter 保證每次請求一定會過濾* @author shuliangzhao* @Title: VerifyFilter* @ProjectName spring-boot-learn* @Description: TODO* @date 2019/8/4 16:58*/ public class VerifyFilter extends OncePerRequestFilter {private static final PathMatcher pathMatcher = new AntPathMatcher();@Overrideprotected void doFilterInternal(HttpServletRequest req, HttpServletResponse resp, FilterChain filterChain) throws ServletException, IOException {if (isProtectedUrl(req)) {String verifyCode = req.getParameter("verifyCode");if (!validateVerify(verifyCode)) {//手動設置異常req.setAttribute("SPRING_SECURITY_LAST_EXCEPTION",new DisabledException("驗證碼輸入錯誤"));// 轉發到錯誤Urlreq.getRequestDispatcher("/login/error").forward(req,resp);} else {filterChain.doFilter(req,resp);}} else {filterChain.doFilter(req,resp);}}private boolean validateVerify(String verifyCode) {//獲取當前線程綁定的request對象HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String validateCode = ((String)request.getSession().getAttribute("validateCode")).toUpperCase();verifyCode = verifyCode.toUpperCase();System.out.println("驗證碼:" + validateCode + "用戶輸入:" + verifyCode);return validateCode.equals(verifyCode);}// 攔截 /login的POST請求private boolean isProtectedUrl(HttpServletRequest req) {return "POST".equals(req.getMethod()) && pathMatcher.match("/login",req.getServletPath());} }

    3.2 注入過濾器

    修改 WebSecurityConfig 的 configure 方法,添加一個 addFilterBefore() ,具有兩個參數,作用是在參數二之前執行參數一設置的過濾器。

    Spring Security 對于用戶名/密碼登錄方式是通過 UsernamePasswordAuthenticationFilter 處理的,我們在它之前執行驗證碼過濾器即可。

    @Override protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests()// 如果有允許匿名的url,填在下面.antMatchers("/getVerifyCode").permitAll().anyRequest().authenticated().and()// 設置登陸頁.formLogin().loginPage("/login")// 設置登陸成功頁.defaultSuccessUrl("/").permitAll()// 登錄失敗Url.failureUrl("/login/error")// 自定義登陸用戶名和密碼參數,默認為username和password // .usernameParameter("username") // .passwordParameter("password").and().addFilterBefore(new VerifyFilter(),UsernamePasswordAuthenticationFilter.class).logout().permitAll()// 自動登錄.and().rememberMe().tokenRepository(persistentTokenRepository())// 有效時間:單位s.tokenValiditySeconds(60).userDetailsService(userDetailsService);// 關閉CSRF跨域http.csrf().disable(); }

    3.3 運行程序

    四、Spring Security 驗證

    使用過濾器就已經實現了驗證碼功能,但其實它和 AJAX 驗證差別不大。

    • AJAX 是在提交前發一個請求,請求返回成功就提交,否則不提交
    • 過濾器是先驗證驗證碼,驗證成功就讓 Spring Security 驗證用戶名和密碼;驗證失敗,則產生異常·。

    如果我們要做的需求是用戶登錄是需要多個驗證字段,不單單是用戶名和密碼,那么使用過濾器會讓邏輯變得復雜,這時候可以考慮自定義 Spring Security 的驗證邏輯了…

    4.1 WebAuthenticationDetails

    我們知道 Spring security 默認只會處理用戶名和密碼信息。這時候就要請出我們的主角——WebAuthenticationDetails

    WebAuthenticationDetails: 該類提供了獲取用戶登錄時攜帶的額外信息的功能,默認提供了 remoteAddress 與 sessionId 信息

    我們需要實現自定義的 WebAuthenticationDetails,并在其中加入我們的驗證碼:

    /*** 獲取用戶登錄時攜帶的額外信息* @author shuliangzhao* @Title: CustomWebAuthenticationDetails* @ProjectName spring-boot-learn* @Description: TODO* @date 2019/8/4 17:14*/ public class CustomWebAuthenticationDetails extends WebAuthenticationDetails {private final String verifyCode;public CustomWebAuthenticationDetails(HttpServletRequest request) {super(request);// verifyCode為頁面中驗證碼的nameverifyCode = request.getParameter("verifyCode");}public String getVerifyCode() {return this.verifyCode;}@Overridepublic String toString() {StringBuilder sb = new StringBuilder();sb.append(super.toString()).append("; VerifyCode: ").append(this.getVerifyCode());return sb.toString();} }

    在這個方法中,我們將前臺 form 表單中的 verifyCode 獲取到,并通過 get 方法方便被調用。

    4.2 AuthenticationDetailsSource

    自定義了WebAuthenticationDetails,我i們還需要將其放入 AuthenticationDetailsSource 中來替換原本的 WebAuthenticationDetails ,因此還得實現自定義 AuthenticationDetailsSource :

    /*** 該接口用于在Spring Security登錄過程中對用戶的登錄信息的詳細信息進行填充* @author shuliangzhao* @Title: CustomAuthenticationDetailsSource* @ProjectName spring-boot-learn* @Description: TODO* @date 2019/8/4 17:17*/ @Component("authenticationDetailsSource") public class CustomAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> {@Overridepublic WebAuthenticationDetails buildDetails(HttpServletRequest request) {return new CustomWebAuthenticationDetails(request);} }

    該類內容將原本的 WebAuthenticationDetails 替換為了我們的 CustomWebAuthenticationDetails。

    然后我們將 CustomAuthenticationDetailsSource 注入Spring Security中,替換掉默認的 AuthenticationDetailsSource。

    修改 WebSecurityConfig,將其注入,然后在config()中使用 authenticationDetailsSource(authenticationDetailsSource)方法來指定它。

    private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource;@Override protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests()// 如果有允許匿名的url,填在下面.antMatchers("/getVerifyCode").permitAll().anyRequest().authenticated().and()// 設置登陸頁.formLogin().loginPage("/login")// 設置登陸成功頁.defaultSuccessUrl("/").permitAll()// 登錄失敗Url.failureUrl("/login/error")// 自定義登陸用戶名和密碼參數,默認為username和password // .usernameParameter("username") // .passwordParameter("password")// 指定authenticationDetailsSource.authenticationDetailsSource(authenticationDetailsSource).and().logout().permitAll()// 自動登錄.and().rememberMe().tokenRepository(persistentTokenRepository())// 有效時間:單位s.tokenValiditySeconds(60).userDetailsService(userDetailsService);// 關閉CSRF跨域http.csrf().disable();

    4.3 AuthenticationProvider

    至此我們通過自定義WebAuthenticationDetails和AuthenticationDetailsSource將驗證碼和用戶名、密碼一起帶入了Spring Security中,下面我們需要將它取出來。

    這里需要我們自定義AuthenticationProvider,需要注意的是,如果是我們自己實現AuthenticationProvider,那么我們就需要自己做密碼校驗了

    /*** @author shuliangzhao* @Title: CustomAuthenticationProvider* @ProjectName spring-boot-learn* @Description: TODO* @date 2019/8/4 17:20*/ @Component public class CustomAuthenticationProvider implements AuthenticationProvider {@Autowiredprivate CustomUserDetailsService userDetailsService;@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {// 獲取用戶輸入的用戶名和密碼String name = authentication.getName();String inputPassword = authentication.getCredentials().toString();CustomWebAuthenticationDetails details = (CustomWebAuthenticationDetails) authentication.getDetails();String verifyCode = details.getVerifyCode();if(!validateVerify(verifyCode)) {throw new DisabledException("驗證碼輸入錯誤");}//userDetails為數據庫中查詢到的用戶信息UserDetails userDetails = userDetailsService.loadUserByUsername(name);if (!userDetails.getPassword().equals(inputPassword)) {throw new BadCredentialsException("密碼錯誤");}return new UsernamePasswordAuthenticationToken(name, inputPassword, userDetails.getAuthorities());}private boolean validateVerify(String verifyCode) {//獲取當前線程綁定的request對象HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String validateCode = ((String)request.getSession().getAttribute("validateCode")).toUpperCase();verifyCode = verifyCode.toUpperCase();System.out.println("驗證碼:" + validateCode + "用戶輸入:" + verifyCode);return validateCode.equals(verifyCode);}@Overridepublic boolean supports(Class<?> authentication) {// 這里不要忘記,和UsernamePasswordAuthenticationToken比較return authentication.equals(UsernamePasswordAuthenticationToken.class);}}

    最后在 WebSecurityConfig 中將其注入,并在 config 方法中通過 auth.authenticationProvider() 指定使用。

    @Autowiredprivate CustomAuthenticationProvider customAuthenticationProvider;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.authenticationProvider(customAuthenticationProvider);}

    4.4 運行程序

    是不是比較復雜,為了實現該需求自定義了 WebAuthenticationDetails、AuthenticationDetailsSource、AuthenticationProvider,讓我們運行一下程序,當輸入錯誤驗證碼時:

    總結

    以上是生活随笔為你收集整理的SpringBoot 整合Security——自定义表单登录的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。