摘要:但是我們最好不要在里面對(duì)他進(jìn)行處理,而是放到配置的權(quán)限異常來處理。記得配置登錄認(rèn)證前和過程中的一些請(qǐng)求不需要身份認(rèn)證。登錄認(rèn)證失敗不能直接拋出錯(cuò)誤,需要向前端響應(yīng)異常。
關(guān)于 Spring Security 的學(xué)習(xí)已經(jīng)告一段落了,剛開始接觸該安全框架感覺很迷茫,總覺得沒有 Shiro 靈活,到后來的深入學(xué)習(xí)和探究才發(fā)現(xiàn)它非常強(qiáng)大。簡(jiǎn)單快速集成,基本不用寫任何代碼,拓展起來也非常靈活和強(qiáng)大。系統(tǒng)集成
集成完該框架默認(rèn)情況下,系統(tǒng)幫我們生成一個(gè)登陸頁(yè),默認(rèn)除了登陸其他請(qǐng)求都需要進(jìn)行身份認(rèn)證,沒有身份認(rèn)證前的任何操作都會(huì)跳轉(zhuǎn)到默認(rèn)登錄頁(yè)。
默認(rèn)生成的密碼也會(huì)在控制臺(tái)輸出。
接下來我們可能需要自己控制一下權(quán)限,自定義一下登錄界面
@Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .formLogin() .loginPage("/login.html") //自定義登錄界面 .loginProcessingUrl("/login.action") //指定提交地址 .defaultSuccessUrl("/main.html") //指定認(rèn)證成功跳轉(zhuǎn)界面 //.failureForwardUrl("/error.html") //指定認(rèn)證失敗跳轉(zhuǎn)界面(注: 轉(zhuǎn)發(fā)需要與提交登錄請(qǐng)求方式一致) .failureUrl("/error.html") //指定認(rèn)證失敗跳轉(zhuǎn)界面(注: 重定向需要對(duì)應(yīng)的方法為 GET 方式) .usernameParameter("username") //username .passwordParameter("password") //password .permitAll() .and() .logout() .logoutUrl("/logout.action") //指定登出的url, controller里面不用寫對(duì)應(yīng)的方法 .logoutSuccessUrl("/login.html") //登出成功跳轉(zhuǎn)的界面 .permitAll() .and() .authorizeRequests() .antMatchers("/register*").permitAll() //設(shè)置不需要認(rèn)證的 .mvcMatchers("/main.html").hasAnyRole("admin") .anyRequest().authenticated() //其他的全部需要認(rèn)證 .and() .exceptionHandling() .accessDeniedPage("/error.html"); //配置權(quán)限失敗跳轉(zhuǎn)界面 (注: url配置不會(huì)被springmvc異常處理攔截, 但是注解配置springmvc異常機(jī)制可以攔截到) }
從上面配置可以看出自定義配置可以簡(jiǎn)單地分為四個(gè)模塊(登錄頁(yè)面自定義、登出自定義、權(quán)限指定、異常設(shè)定),每個(gè)模塊都對(duì)應(yīng)著一個(gè)過濾器,詳情請(qǐng)看 Spring Security 進(jìn)階-原理篇
需要注意的是:
配置登錄提交的URL loginProcessingUrl(..)、登出URL logoutUrl(..) 都是對(duì)應(yīng)攔截器的匹配地址,會(huì)在對(duì)應(yīng)的過濾器里面執(zhí)行相應(yīng)的邏輯,不會(huì)執(zhí)行到 Controller 里面的方法。
配置的登錄認(rèn)證成功跳轉(zhuǎn)的URL defaultSuccessUrl(..)、登錄認(rèn)證失敗跳轉(zhuǎn)的URL failureUrl(..)、登錄認(rèn)證失敗轉(zhuǎn)發(fā)的URL failureForwardUrl(..)......以及下面登出和權(quán)限配置的URL 可以是靜態(tài)界面地址,也可以是 Controller 里面對(duì)應(yīng)的方法。
這里配置 URL 對(duì)應(yīng)的訪問權(quán)限,訪問失敗不會(huì)被 SpringMVC 的異常方法攔截到,注解配置的可以被攔截到。但是我們最好不要在 SpringMVC 里面對(duì)他進(jìn)行處理,而是放到配置的權(quán)限異常來處理。
登錄身份認(rèn)證失敗跳轉(zhuǎn)對(duì)應(yīng)的地址前會(huì)把異常保存到 request(轉(zhuǎn)發(fā)) 或 session(重定向) 里面,可以通過 key WebAttributes.AUTHENTICATION_EXCEPTION 來取出,但是前提是使用系統(tǒng)提供的身份認(rèn)證異常處理handler SimpleUrlAuthenticationFailureHandler。
上面這種配置身份認(rèn)證失敗都會(huì)跳轉(zhuǎn)到登錄頁(yè),權(quán)限失敗會(huì)跳轉(zhuǎn)指定的 URL,沒有配置 URL 則會(huì)響應(yīng) 403 的異常給前端,前提是在使用系統(tǒng)為我們提供的默認(rèn)權(quán)限異常處理handler AccessDeniedHandlerImpl。
異步響應(yīng)配置大多數(shù)開發(fā)情況下都是前后端分離,響應(yīng)也都是異步的,不是上面那種表單界面的響應(yīng)方式,雖然通過上面跳轉(zhuǎn)到URL對(duì)應(yīng)的 Controller 里面的方法也能解決,但是大多數(shù)情況下我們需要的是極度簡(jiǎn)化,這時(shí)候一些自定義的處理 handler 就油然而生。
@Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .formLogin() .loginProcessingUrl("/login") .successHandler(new AuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { System.out.println("****** 身份認(rèn)證成功 ******"); response.setStatus(HttpStatus.OK.value()); } }) .failureHandler(new AuthenticationFailureHandler() { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { System.out.println("****** 身份認(rèn)證失敗 ******"); response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); } }) .permitAll() .and() .logout() .logoutUrl("/logout") .logoutSuccessHandler(new LogoutSuccessHandler() { @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { System.out.println("****** 登出成功 ******"); response.setStatus(HttpStatus.OK.value()); } }) .permitAll() .and() .authorizeRequests() .antMatchers("/main").hasAnyRole("admin") .and() .exceptionHandling() .authenticationEntryPoint(new AuthenticationEntryPoint() { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { System.out.println("****** 沒有進(jìn)行身份認(rèn)證 ******"); response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); } }) .accessDeniedHandler(new AccessDeniedHandler() { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { System.out.println("****** 沒有權(quán)限 ******"); response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); } }); }
注意:
沒有指定登錄界面,那么久需要至少配置兩個(gè) handler,登出失敗 hander logoutSuccessHandler(..),登錄身份認(rèn)證失敗的 handler failureHandler(..),以免默認(rèn)這樣兩個(gè)步驟向不存在的登錄頁(yè)跳轉(zhuǎn)。
配置的登錄身份認(rèn)證失敗 handler failureHandler(..) 和 沒有進(jìn)行身份認(rèn)證的異常 handler authenticationEntryPoint(..),這兩個(gè)有區(qū)別,前者是在認(rèn)證過程中出現(xiàn)異常處理,后者是在訪問需要進(jìn)行身份認(rèn)證的URL時(shí)沒有進(jìn)行身份認(rèn)證異常處理。
自定義身份認(rèn)證過程開發(fā)的時(shí)候我們需要自己來實(shí)現(xiàn)登錄登出的流程,下面來個(gè)最簡(jiǎn)單的自定義。
@Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .logout() .logoutUrl("/logout") .logoutSuccessHandler(new LogoutSuccessHandler() { @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { System.out.println("****** 登出成功 ******"); response.setStatus(HttpStatus.OK.value()); } }) .permitAll() .and() .authorizeRequests() .antMatchers("/main").hasAnyRole("admin") .and() .exceptionHandling() .authenticationEntryPoint(new AuthenticationEntryPoint() { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { System.out.println("****** 沒有進(jìn)行身份認(rèn)證 ******"); response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); } }) .accessDeniedHandler(new AccessDeniedHandler() { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { System.out.println("****** 沒有權(quán)限 ******"); response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); } }) .and() .addFilterBefore(new LoginFilter(), UsernamePasswordAuthenticationFilter.class); }
注意:
這里配置了登出,也可以不配置,在自定義登出的 Controller 方法里面進(jìn)行手動(dòng)清空 SecurityContextHolder.clearContext();,但是建議配置,一般登錄和登出最好都在過濾器里面進(jìn)行處理。
添加自定義登錄過濾器,相當(dāng)于配置登錄。
記得配置登錄認(rèn)證前和過程中的一些請(qǐng)求不需要身份認(rèn)證。
自定義登錄過濾器詳情
public class LoginFilter extends GenericFilterBean { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletResponse httpServletResponse = (HttpServletResponse) response; if ("/login".equals(httpServletRequest.getServletPath())) { //開始登錄過程 String username = httpServletRequest.getParameter("username"); String password = httpServletRequest.getParameter("password"); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, password); //模擬數(shù)據(jù)庫(kù)查出來的 User.UserBuilder userBuilder = User.withUsername(username); userBuilder.password("123"); userBuilder.roles("user", "admin"); UserDetails user = userBuilder.build(); if (user == null) { System.out.println("****** 自定義登錄過濾器 該用戶不存在 ******"); httpServletResponse.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); } if (!user.getUsername().equals(authentication.getPrincipal())) { System.out.println("****** 自定義登錄過濾器 賬號(hào)有問題 ******"); httpServletResponse.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); } if (!user.getPassword().equals(authentication.getCredentials())) { System.out.println("****** 自定義登錄過濾器 密碼有問題 ******"); httpServletResponse.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); } UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(user.getUsername(), authentication.getCredentials(), user.getAuthorities()); result.setDetails(authentication.getDetails()); //注: 最重要的一步 SecurityContextHolder.getContext().setAuthentication(result); httpServletResponse.setStatus(HttpStatus.OK.value()); } else { chain.doFilter(request, response); } } }
注意:
不是登錄認(rèn)證就接著執(zhí)行下一個(gè)過濾器或其他。
登錄認(rèn)證失敗不能直接拋出錯(cuò)誤,需要向前端響應(yīng)異常。
完成登錄邏輯直接響應(yīng),不需要接著往下執(zhí)行什么。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/77491.html
摘要:過濾器基本都是通過過濾器來完成配置的身份認(rèn)證權(quán)限認(rèn)證以及登出。密碼比對(duì)通過進(jìn)行密碼比對(duì)注可自定義通過獲取通過獲取生成身份認(rèn)證通過后最終返回的記錄認(rèn)證的身份信息 知彼知己方能百戰(zhàn)百勝,用 Spring Security 來滿足我們的需求最好了解其原理,這樣才能隨意拓展,本篇文章主要記錄 Spring Security 的基本運(yùn)行流程。 過濾器 Spring Security 基本都是通過...
摘要:在中加密是一個(gè)很簡(jiǎn)單卻又不能忽略的模塊,數(shù)據(jù)只有加密起來才更安全,這樣就散算據(jù)庫(kù)密碼泄漏也都是密文。當(dāng)然也可以自定義構(gòu)造方法,來制定用其他的方案進(jìn)行加密。應(yīng)用先示范下使用系統(tǒng)的來演示下簡(jiǎn)單的注入構(gòu)造加密方案 在 Spring Security 中加密是一個(gè)很簡(jiǎn)單卻又不能忽略的模塊,數(shù)據(jù)只有加密起來才更安全,這樣就散算據(jù)庫(kù)密碼泄漏也都是密文。本文分析對(duì)應(yīng)的版本是 5.14。 概念 Spr...
摘要:框架具有輕便,開源的優(yōu)點(diǎn),所以本譯見構(gòu)建用戶管理微服務(wù)五使用令牌和來實(shí)現(xiàn)身份驗(yàn)證往期譯見系列文章在賬號(hào)分享中持續(xù)連載,敬請(qǐng)查看在往期譯見系列的文章中,我們已經(jīng)建立了業(yè)務(wù)邏輯數(shù)據(jù)訪問層和前端控制器但是忽略了對(duì)身份進(jìn)行驗(yàn)證。 重拾后端之Spring Boot(四):使用JWT和Spring Security保護(hù)REST API 重拾后端之Spring Boot(一):REST API的搭建...
摘要:哪吒社區(qū)技能樹打卡打卡貼函數(shù)式接口簡(jiǎn)介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號(hào)作者架構(gòu)師奮斗者掃描主頁(yè)左側(cè)二維碼,加入群聊,一起學(xué)習(xí)一起進(jìn)步歡迎點(diǎn)贊收藏留言前情提要無意間聽到領(lǐng)導(dǎo)們的談話,現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨(dú)立帶隊(duì)的人太少,簡(jiǎn)而言之,不缺干 ? 哪吒社區(qū)Java技能樹打卡?【打卡貼 day2...
摘要:?jiǎn)⒂冒踩赃@個(gè)簡(jiǎn)單的默認(rèn)配置指定了如何保護(hù)請(qǐng)求,以及客戶端認(rèn)證用戶的方案。基于數(shù)據(jù)庫(kù)進(jìn)行認(rèn)證用戶數(shù)據(jù)通常會(huì)存儲(chǔ)在關(guān)系型數(shù)據(jù)庫(kù)中,并通過進(jìn)行訪問。必須經(jīng)過認(rèn)證其他所有請(qǐng)求都是允許的,不需要認(rèn)證。要求用戶不僅需要認(rèn)證,還要具備權(quán)限。 Spring Security Spring Security 是基于Spring 應(yīng)用程序提供的聲明式安全保護(hù)的安全框架。Spring Sercurity ...
閱讀 3740·2021-11-24 09:39
閱讀 3470·2019-08-30 15:56
閱讀 1375·2019-08-30 15:55
閱讀 1039·2019-08-30 15:53
閱讀 1925·2019-08-29 18:37
閱讀 3607·2019-08-29 18:32
閱讀 3135·2019-08-29 16:30
閱讀 2935·2019-08-29 15:14