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

歡迎訪問 生活随笔!

生活随笔

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

javascript

SpringBoot整合Spring Security——登录管理

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

文章目錄

    • 一、自定義認證成功、失敗處理
      • 1.1 CustomAuthenticationSuccessHandle
      • 1.2 CustomAuthenticationFailureHandler
      • 1.3 修改 WebSecurityConfig
      • 1.4 運行程序
    • 二、Session 超時
    • 三、限制最大登錄數
    • 四、踢出用戶
    • 五、退出登錄
    • 六、Session 共享
      • 6.1 導入依賴
      • 6.3 運行程序

一、自定義認證成功、失敗處理

有些時候我們想要在認證成功后做一些業務處理,例如添加積分;有些時候我們想要在認證失敗后也做一些業務處理,例如記錄日志。

在之前的文章中,關于認證成功、失敗后的處理都是如下配置的:

http.authorizeRequests()// 如果有允許匿名的url,填在下面 // .antMatchers().permitAll().anyRequest().authenticated().and()// 設置登陸頁.formLogin().loginPage("/login").failureUrl("/login/error").defaultSuccessUrl("/").permitAll()...;

即 failureUrl() 指定認證失敗后Url,defaultSuccessUrl() 指定認證成功后Url。我們可以通過設置 successHandler() 和 failureHandler() 來實現自定義認證成功、失敗處理。

PS:當我們設置了這兩個后,需要去除 failureUrl() 和 defaultSuccessUrl() 的設置,否則無法生效。這兩套配置同時只能存在一套

1.1 CustomAuthenticationSuccessHandle

自定義 CustomAuthenticationSuccessHandler 類來實現 AuthenticationSuccessHandler 接口,用來處理認證成功后邏輯:

@Component public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {private Logger logger = LoggerFactory.getLogger(getClass());@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {logger.info("登錄成功,{}", authentication);response.sendRedirect("/");} }

onAuthenticationSuccess() 方法的第三個參數 Authentication 為認證后該用戶的認證信息,這里打印日志后,重定向到了首頁。

1.2 CustomAuthenticationFailureHandler

自定義 CustomAuthenticationFailureHandler 類來實現 AuthenticationFailureHandler 接口,用來處理認證失敗后邏輯:

@Component public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {private Logger logger = LoggerFactory.getLogger(getClass());@Autowiredprivate ObjectMapper objectMapper;@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {logger.info("登錄失敗,{}");response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());response.setContentType("application/json;charset=UTF-8");response.getWriter().write(objectMapper.writeValueAsString(e.getMessage()));} }

onAuthenticationFailure()方法的第三個參數 exception 為認證失敗所產生的異常,這里也是簡單的返回到前臺。

1.3 修改 WebSecurityConfig

@Autowiredprivate CustomAuthenticationFailureHandler customAuthenticationFailureHandler;@Autowiredprivate CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler; @Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests()// 如果有允許匿名的url,填在下面//.antMatchers().permitAll().anyRequest().authenticated().and()// 設置登陸頁.formLogin().loginPage("/login").successHandler(customAuthenticationSuccessHandler).failureHandler(customAuthenticationFailureHandler) // .failureUrl("/login/error") // .defaultSuccessUrl("/").permitAll();http.csrf().disable();}

1.首先將 customAuthenticationSuccessHandler 和 customAuthenticationFailureHandler注入進來
2.配置 successHandler() 和 failureHandler()
3.注釋 failureUrl() 和 defaultSuccessUrl()

1.4 運行程序

運行程序,當我們成功登陸后,發現日志信息被打印出來,頁面被重定向到了首頁:

當我們認證失敗后,發現日志中“登陸失敗”被打印出來,頁面展示了認證失敗的異常消息:

二、Session 超時

當用戶登錄后,我們可以設置 session 的超時時間,當達到超時時間后,自動將用戶退出登錄。

Session 超時的配置是 SpringBoot 原生支持的,我們只需要在 application.properties 配置文件中配置:

# session 過期時間,單位:秒 server.servlet.session.timeout=60

Tip:
從用戶最后一次操作開始計算過期時間。
過期時間最小值為 60 秒,如果你設置的值小于 60 秒,也會被更改為 60 秒。

我們可以在 Spring Security 中配置處理邏輯,在 session 過期退出時調用。修改 WebSecurityConfig 的 configure() 方法,添加:

.sessionManagement()// 以下二選一//.invalidSessionStrategy()//.invalidSessionUrl();

Spring Security 提供了兩種處理配置,一個是 invalidSessionStrategy(),另外一個是 invalidSessionUrl()。

這兩個的區別就是一個是前者是在一個類中進行處理,后者是直接跳轉到一個 Url。簡單起見,我就直接用 invalidSessionUrl()了,跳轉到 /login/invalid,我們需要把該 Url 設置為免授權訪問, 配置如下:

@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests()// 如果有允許匿名的url,填在下面.antMatchers("/login/invalid").permitAll().anyRequest().authenticated().and()// 設置登陸頁.formLogin().loginPage("/login").successHandler(customAuthenticationSuccessHandler).failureHandler(customAuthenticationFailureHandler).permitAll().and() // .failureUrl("/login/error") // .defaultSuccessUrl("/").sessionManagement().invalidSessionUrl("/login/invalid");http.csrf().disable();}

在 controller 中寫一個接口進行處理:

@RequestMapping("/login/invalid") @ResponseStatus(HttpStatus.UNAUTHORIZED) @ResponseBody public String invalid() {return "Session 已過期,請重新登錄"; }

運行程序,登陸成功后等待一分鐘(或者重啟服務器),刷新頁面:

三、限制最大登錄數

接下來實現限制最大登陸數,原理就是限制單個用戶能夠存在的最大 session 數。

在上一節的基礎上,修改 configure() 為:

@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests()// 如果有允許匿名的url,填在下面.antMatchers("/login/invalid").permitAll().anyRequest().authenticated().and()// 設置登陸頁.formLogin().loginPage("/login").successHandler(customAuthenticationSuccessHandler).failureHandler(customAuthenticationFailureHandler).permitAll().and() // .failureUrl("/login/error") // .defaultSuccessUrl("/").sessionManagement().invalidSessionUrl("/login/invalid")//指定最大登錄數.maximumSessions(1)//當達到最大值時,是否保留已經登錄的用戶.maxSessionsPreventsLogin(false)//當達到最大值時,舊用戶被踢出后的操作.expiredSessionStrategy(customExpiredSessionStrategy);http.csrf().disable();}

增加了下面三行代碼,其中:

maximumSessions(int):指定最大登錄數
maxSessionsPreventsLogin(boolean):是否保留已經登錄的用戶;為true,新用戶無法登錄;為 false,舊用戶被踢出
expiredSessionStrategy(SessionInformationExpiredStrategy):舊用戶被踢出后處理方法

maxSessionsPreventsLogin()可能不太好理解,這里我們先設為 false,效果和 QQ 登錄是一樣的,登陸后之前登錄的賬戶被踢出。

編寫 CustomExpiredSessionStrategy 類,來處理舊用戶登陸失敗的邏輯:

@Component public class CustomExpiredSessionStrategy implements SessionInformationExpiredStrategy {private ObjectMapper objectMapper = new ObjectMapper();@Overridepublic void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {Map<String,Object> map = new HashMap<>();map.put("code",0);map.put("msg","已經另一臺機器登錄,您被迫下線。" + event.getSessionInformation().getLastRequest());String s = objectMapper.writeValueAsString(map);event.getResponse().setContentType("application/json;charset=UTF-8");event.getResponse().getWriter().write(s);} }

在 onExpiredSessionDetected() 方法中,處理相關邏輯,我這里只是簡單的返回一句話。

執行程序,打開兩個瀏覽器,登錄同一個賬戶。因為我設置了 maximumSessions(1),也就是單個用戶只能存在一個 session,因此當你刷新先登錄的那個瀏覽器時,被提示踢出了。

下面我們來測試下 maxSessionsPreventsLogin(true) 時的情況,我們發現第一個瀏覽器登錄后,第二個瀏覽器無法登錄:

四、踢出用戶

下面來看下如何主動踢出一個用戶。

首先需要在容器中注入名為 SessionRegistry 的 Bean,這里我就簡單的寫在 WebSecurityConfig 中:

@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests()// 如果有允許匿名的url,填在下面.antMatchers("/login/invalid").permitAll().anyRequest().authenticated().and()// 設置登陸頁.formLogin().loginPage("/login").successHandler(customAuthenticationSuccessHandler).failureHandler(customAuthenticationFailureHandler).permitAll().and() // .failureUrl("/login/error") // .defaultSuccessUrl("/").sessionManagement().invalidSessionUrl("/login/invalid")//指定最大登錄數.maximumSessions(1)//當達到最大值時,是否保留已經登錄的用戶.maxSessionsPreventsLogin(false)//當達到最大值時,舊用戶被踢出后的操作.expiredSessionStrategy(customExpiredSessionStrategy).sessionRegistry(sessionRegistry());http.csrf().disable();} @Bean public SessionRegistry sessionRegistry() {return new SessionRegistryImpl(); }

編寫一個接口用于測試踢出用戶:

@GetMapping("/click")@ResponseBodypublic String removeUserSessionByUsername(@RequestParam String username) {int count = 0;List<Object> users = sessionRegistry.getAllPrincipals();for (Object user:users) {if (user instanceof User) {String principalName = ((User) user).getUsername();if (principalName.equals(username)) {List<SessionInformation> allSessions = sessionRegistry.getAllSessions(user, false);if (allSessions != null && allSessions.size() > 0) {for (SessionInformation sessionInformation:allSessions) {sessionInformation.expireNow();count++;}}}}}return "操作成功,清理session共" + count + "個";}
  • sessionRegistry.getAllPrincipals(); 獲取所有 principal 信息
  • 通過 principal.getUsername 是否等于輸入值,獲取到指定用戶的 principal
  • sessionRegistry.getAllSessions(principal, false)獲取該 principal 上的所有 session
  • 通過 sessionInformation.expireNow() 使得 session 過期
  • 運行程序,分別使用 admin 和 zhao賬戶登錄,admin 訪問 /kick?username=zhao 來踢出用戶 zhao,zhao 刷新頁面,發現被踢出。

    五、退出登錄

    補充一下退出登錄的內容,在之前,我們直接在 WebSecurityConfig 的 configure() 方法中,配置了:

    http.logout();

    這就是 Spring Security 的默認退出配置,Spring Security 在退出時候做了這樣幾件事:

  • 使當前的 session 失效
  • 清除與當前用戶有關的 remember-me 記錄
  • 清空當前的 SecurityContext
  • 重定向到登錄頁
    Spring Security 默認的退出 Url 是 /logout,我們可以修改默認的退出 Url,例如修改為 /signout:
    WebSecurityConfig配置如下:
  • @Autowiredprivate CustomLogoutSuccessHandler customLogoutSuccessHandler;@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests()// 如果有允許匿名的url,填在下面.antMatchers("/login/invalid").permitAll().anyRequest().authenticated().and()// 設置登陸頁.formLogin().loginPage("/login").successHandler(customAuthenticationSuccessHandler).failureHandler(customAuthenticationFailureHandler).permitAll().and().logout().logoutUrl("/signout").deleteCookies("JSESSIONID").logoutSuccessHandler(customLogoutSuccessHandler).and() // .failureUrl("/login/error") // .defaultSuccessUrl("/").sessionManagement().invalidSessionUrl("/login/invalid")//指定最大登錄數.maximumSessions(1)//當達到最大值時,是否保留已經登錄的用戶.maxSessionsPreventsLogin(false)//當達到最大值時,舊用戶被踢出后的操作.expiredSessionStrategy(customExpiredSessionStrategy).sessionRegistry(sessionRegistry());http.csrf().disable();} @Component public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {private final Logger log = LoggerFactory.getLogger(CustomLogoutSuccessHandler.class);@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {String username = ((User)authentication.getPrincipal()).getUsername();log.info("退出成功,用戶名:{}", username);// 重定向到登錄頁response.sendRedirect("/login");} }

    六、Session 共享

    在最后補充下關于 Session 共享的知識點,一般情況下,一個程序為了保證穩定至少要部署兩個,構成集群。那么就牽扯到了 Session 共享的問題,不然用戶在 8080 登錄成功后,后續訪問了 8060 服務器,結果又提示沒有登錄。

    這里就簡單實現下 Session 共享,采用 Redis 來存儲。

    6.1 導入依賴

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId> </dependency>

    在 application.xml 中新增配置指定 redis 地址以及 session 的存儲方式

    spring.redis.host=localhost spring.redis.port=6379spring.session.store-type=redis

    然后為主類添加 @EnableRedisHttpSession 注解。

    @EnableRedisHttpSession @SpringBootApplication public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);} }

    6.3 運行程序

    分別啟動兩個端口8080,8086
    先訪問 localhost:8080,登錄成功后,再訪問 localhost:8060,發現無需登錄。

    然后我們進入 Redis 查看下 key:

    最后再測試下之前配置的 session 設置是否還有效,使用其他瀏覽器登陸,登陸成功后發現原瀏覽器用戶的確被踢出。

    總結

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

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