如何使用JWT的SpringSecurity實現(xiàn)前后端分離
1. SpringSecurity完成前后端完全分離
分析:
前后端分離:響應的數(shù)據(jù)必須為JSON數(shù)據(jù),之前響應的是網(wǎng)頁
需要修改的代碼有:
登錄成功需要返回json數(shù)據(jù)登錄失敗需要返回json數(shù)據(jù)權(quán)限不足時返回json數(shù)據(jù)未登錄訪問資源返回json數(shù)據(jù)
1.1 登錄成功需要返回json數(shù)據(jù)
第一種方案:基于redis 實現(xiàn)session共享
該方案的缺點:
redis壓力太大項目依賴于第三方組件
第二種方案:基于jwt【采用】
jwt幫你生成唯一標志,而且校驗唯一標志。信息存放在jwt中,同時可以從jwt中獲取
1.2 JWT的概述
1.2.1 什么是JWT
Json web token (JWT),是為了在網(wǎng)絡應用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開放標準((RFC7519).該token被設計為緊湊且安全的,特別適用于分布式站點的單點登錄(SSO)場景。JWT的聲明一般被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便于從資源服務器獲取資源,也可以增加一些額外的其它業(yè)務邏輯所必須的聲明信息,該token也可直接被用于認證,也可被加密。
官網(wǎng): https://jwt.io/introduction/
JWT就是token的一種具體實現(xiàn)方式,本質(zhì)就是一個字符串,它將用戶信息保存到一個json字符串中,然后進行編碼后得到一個
JWT token
,并且這個JWT token
帶有簽名信息,接收后可以校驗是否被篡改。所以可以用于在各方之間安全地將信息作為JSON對象傳輸。
1.2.2 前后端完全分離認證問題
互聯(lián)網(wǎng)服務離不開用戶認證。一般流程是下面這樣。
1、用戶向服務器發(fā)送用戶名和密碼。
2、服務器驗證通過后,在當前對話(session)里面保存相關(guān)數(shù)據(jù),比如用戶角色、登錄
時間等等。
3、服務器向用戶返回一個session_id,寫入用戶的Cookie。
4、用戶隨后的每一次請求,都會通過Cookie,將session_id傳回服務器。5、服務器收到 session_id,找到前期保存的數(shù)據(jù),由此得知用戶的身份。
這種模式的問題在于,擴展性(scaling)不好。單機當然沒有問題,如果是服務器集群,或者是前后端分離的服務導向架構(gòu),就要求session 數(shù)據(jù)共享,每臺服務器都能夠讀取session,
舉例來說,A網(wǎng)站和B網(wǎng)站是同一家公司的關(guān)聯(lián)服務?,F(xiàn)在要求,用戶只要在其中一個網(wǎng)站登錄,再訪問另一個網(wǎng)站就會自動登錄,請問怎么實現(xiàn)?
一種解決方案是 session 數(shù)據(jù)持久化,寫入數(shù)據(jù)庫或別的持久層。各種服務收到請求后,都向持久層請求數(shù)據(jù)。這種方案的優(yōu)點是架構(gòu)清晰,缺點是工程量比較大[]。另外,持久層萬一掛了,就會單點失敗。
另一種方案是服務器索性不保存 session 數(shù)據(jù)了,所有數(shù)據(jù)都保存在客戶端,每次請求都發(fā)回服務器。JWT就是這種方案的一個代表。
JWT:影響了網(wǎng)絡寬帶
1.2.3 JWT的原理
JWT的原理是,服務器認證以后,生成一個JSON對象,發(fā)回給用戶,就像下面這樣。
{
“姓名”:“張三”,
“角色”:“管理員”,
“到期時間”:“2022年8月1日0點0分”
}
以后,用戶與服務端通信的時候,都要發(fā)回這個JSON對象。服務器完全只靠這個對象認定用戶身份。為了防止用戶篡改數(shù)據(jù),服務器在生成這個對象的時候,會加上簽名(詳見后文)。
服務器就不保存任何 session 數(shù)據(jù)了,也就是說,服務器變成無狀態(tài)了,從而比較容易實現(xiàn)擴展。
1.2.4 JWT的數(shù)據(jù)結(jié)構(gòu)
實際的 JWT大概就像下面這樣。
它是一個很長的字符串,中間用點(.)分隔成三個部分。注意,JWT內(nèi)部是沒有換行的,這里只是為了便于展示,將它寫成了幾行
JWT的三個部分依次如下:
Header(頭部)Payload(負載,載荷)Signature(簽名)
寫成一行,就是下面的樣子。
Header.Payload.Signature
1.2.4.1 Header
{ "alg": "HS256", "typ": "JWT" }
JWT頭是一個描述JWT元數(shù)據(jù)的JSON對象,alg屬性表示簽名使用的算法,默認為HMAC SHA256(寫為HS256);typ屬性表示令牌的類型,JWT令牌統(tǒng)一寫為JWT。最后,使用Base64 URL算法將上述JSON對象轉(zhuǎn)換為字符串保存 。
1.2.4.2 Payload
Payload 部分也是一個JSON對象,用來存放實際需要傳遞的數(shù)據(jù)。JWT規(guī)定了7個官方字段,供選用。
iss (issuer):簽發(fā)人 exp (expiration time):過期時間 sub (subject):主題 aud (audience):受眾 nbf (Not Before):生效時間 iat (lssued At):簽發(fā)時間 jti (JWT ID):編號
除了官方字段,你還可以在這個部分定義自己的字段,下面就是一個例子。
{ "sub": "1234567890", "name" : "John Doe", “userid”:2 "admin": true }
注意,JWT 默認是不加密的,任何人都可以讀到,所以不要把==秘密信息【密碼】==放在這個部分。這個JSON 對象也要使用Base64URL 算法轉(zhuǎn)成字符串。
JWT只是適合在網(wǎng)絡中傳輸一些非敏感的信息
1.2.4.3 Signature
Signature部分是對前兩部分的簽名,防止數(shù)據(jù)篡改。
首先,需要指定一個密鑰(secret)。這個密鑰只有服務器才知道,不能泄露給用戶。然后,使用Header里面指定的簽名算法(默認是 HMAC SHA256),按照下面的公式產(chǎn)生簽名。
HMACSHA256( base64UrlEncode(header) + ".”"+base64UrlEncode(payload), secret)
算出簽名以后,把 Header、Payload、Signature 三個部分拼成一個字符串,每個部分之間用"點"(.)分隔,就可以返回給用戶。
1.2.5 JWT的使用方式
客戶端收到服務器返回的JWT,可以儲存在Cookie里面,也可以儲存在 localStorage、SessionStorage
此后,客戶端每次與服務器通信,都要帶上這個JWT。你可以把它放在Cookie里面自動發(fā)送,但是這樣不能跨域,所以更好的做法是放在HTTPs請求的頭信息Authorization字段里面。
客戶端收到服務器返回的JWT,可以儲存在Cookie里面,也可以儲存在 localStorage。SessionStorage
此后,客戶端每次與服務器通信,都要帶上這個JWT。你可以把它放在Cookie里面自動發(fā)送,但是這樣不能跨域,所以更好的做法是放在HTTP請求的頭信息Authorization字段里面。
步驟
1. 引入jar
<!--引入jwt的依賴--> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>4.4.0</version> </dependency>
2. 創(chuàng)建jwt的工具類
通過jwt創(chuàng)建token令牌
private static String key="layZhang"; //創(chuàng)建token public static String createToken(Map<String,Object> map){ //設置頭部信息 Map<String,Object> head=new HashMap<>(); head.put("alg","HS256"); head.put("typ","JWT"); //設置發(fā)布日期 Date date=new Date(); //設置過期時間 Calendar instance = Calendar.getInstance();//獲取當前時間 instance.set(Calendar.SECOND,7200);//在當前時間的基礎上添加兩個小時 Date time = instance.getTime();//得到Date類型的時間 //創(chuàng)建token String token = JWT.create() .withHeader(head)//設置頭部信息 .withIssuedAt(date)//設置發(fā)布時間 .withExpiresAt(time)//設置過期時間 .withClaim("userInfo", map)//設置個人信息 .sign(Algorithm.HMAC256(key));//簽名 return token; }
校驗token
//校驗token public static boolean verify(String token){ Verification require = JWT.require(Algorithm.HMAC256(key)); try { require.build().verify(token); return true; }catch (Exception e){ System.out.println("token錯誤"); return false; } }
JWT.require()方法
: 這是JWT庫中的一個方法,用于創(chuàng)建一個Verification
【驗證】對象,該對象用于配置和執(zhí)行JWT的驗證過程。
Algorithm.HMAC256(key)
: 這里指定了用于簽名JWT的算法和密鑰。HMAC256
是一種基于哈希的消息認證碼(HMAC)算法,它使用SHA-256哈希函數(shù)。key
是一個密鑰,用于生成和驗證JWT的簽名。這個密鑰在生成JWT令牌和驗證JWT令牌時必須相同。
require.build()
: 這個方法調(diào)用會基于之前通過JWT.require(...)
方法配置的驗證要求,構(gòu)建一個JWTVerifier
實例。這個實例包含了所有必要的驗證配置(如簽名算法和密鑰)。
.verify(token)
: 使用構(gòu)建好的JWTVerifier
實例來驗證給定的token
。如果token
是有效的(即,它是由指定的密鑰和算法簽名的,且未被篡改),則此方法將成功執(zhí)行。如果token
無效,將拋出異常。
綜上所述,
verify
方法通過指定的密鑰和算法驗證給定的JWT令牌是否有效,并根據(jù)驗證結(jié)果返回相應的布爾值。這種方法是Web應用中實現(xiàn)身份驗證和授權(quán)的一種常見方式。
根據(jù)token獲取自定義的信息
//根據(jù)token獲取自定義的信息 public static Map<String,Object> getInfo(String token,String mykey){ JWTVerifier build = JWT.require(Algorithm.HMAC256(key)).build(); Claim claim = build.verify(token).getClaim(mykey); return claim.asMap(); }
JWT.require(Algorithm.HMAC256(key))
: 這部分代碼與前面提到的驗證token的方法類似,它創(chuàng)建了一個Verification
配置,指定了用于驗證JWT的算法(HMAC256)和密鑰(key
)。這里的key
應該是一個在JWT生成和驗證過程中都使用的共享密鑰。
.build()
: 這個方法調(diào)用基于前面配置的驗證要求,構(gòu)建了一個JWTVerifier
實例。這個實例將用于驗證JWT令牌。
build.verify(token)
: 使用構(gòu)建的JWTVerifier
實例來驗證給定的token
。如果token
是有效的(即,它確實是由指定的密鑰和算法簽名的,并且沒有被篡改),這個方法將返回一個DecodedJWT
對象,該對象包含了JWT中的所有信息。
.getClaim(mykey)
: 從驗證并解碼的JWT中獲取與mykey
鍵相關(guān)聯(lián)的Claim
對象。JWT中的信息以鍵值對的形式存儲,其中每個鍵值對都是一個Claim
。如果JWT中不存在與mykey
對應的Claim
,則此方法可能拋出異?;蚍祷?code>null(具體行為取決于JWT庫的實現(xiàn))。
claim.asMap()
: 如果Claim
對象存在且不為空,這個方法將Claim
中的信息轉(zhuǎn)換為一個Map
。這樣,你就可以像操作普通Map一樣方便地訪問JWT中存儲的自定義信息了
1.3 登錄成功后返回json數(shù)據(jù)
AuthenticationSuccessHandler接口:只有一個抽象方法,為函數(shù)式接口,所以可以使用Lamda表達式重寫抽象方法。這個接口是Spring Security用于處理成功認證后的行為的一個鉤子。 用于自定義用戶成功登錄后的處理邏輯。當認證過程成功完成時(例如,用戶提供了正確的用戶名和密碼),Spring Security會調(diào)用實現(xiàn)了這個接口的類的
onAuthenticationSuccess
方法。
private AuthenticationSuccessHandler successHandler(){ // return new AuthenticationSuccessHandler() { // @Override // // public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { // //設置響應的編碼 // httpServletResponse.setContentType("application/json;charset=utf-8"); // //獲取輸出對象 // PrintWriter writer = httpServletResponse.getWriter(); // //返回json數(shù)據(jù)即可 // Map<String,Object> map=new HashMap<>(); // map.put("username",authentication.getName()); // Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities(); // //獲取權(quán)限 // List<String> collect = authorities.stream().map(item -> item.getAuthority()).collect(Collectors.toList()); // // map.put("permissions",collect); // String token = JWTUtil.createToken(map); // // //返回一個統(tǒng)一的json對象 // R r=new R(200,"登錄成功",token); // //轉(zhuǎn)換為json字符串 // String jsonString = JSON.toJSONString(r); // //servlet // writer.println(jsonString); // writer.flush(); // writer.close(); // } // }; //使用Lambda表達式 return (httpServletRequest, httpServletResponse, authentication) -> { //設置響應的編碼 httpServletResponse.setContentType("application/json;charset=utf-8"); //獲取輸出對象 PrintWriter writer = httpServletResponse.getWriter(); //返回json數(shù)據(jù)即可 Map<String,Object> map=new HashMap<>(); map.put("username",authentication.getName()); //獲取權(quán)限信息列表 Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities(); //獲取響應的權(quán)限標識符 List<String> collect = authorities.stream().map(item -> item.getAuthority()).collect(Collectors.toList()); map.put("permissions",collect); String token = JWTUtil.createToken(map); //返回一個統(tǒng)一的json對象 R r=new R(200,"登錄成功",token); //轉(zhuǎn)換為json字符串 String jsonString = JSON.toJSONString(r); //servlet writer.println(jsonString); writer.flush(); writer.close(); }; }
修改配置
.successHandler(successHandler())
onAuthenticationSuccess
方法:
這是AuthenticationSuccessHandler
接口中需要實現(xiàn)的方法。它有三個參數(shù):HttpServletRequest
、HttpServletResponse
和Authentication
。
HttpServletRequest
: 提供了對當前HTTP請求信息的訪問。HttpServletResponse
: 允許你控制對客戶端的響應,比如設置響應頭、發(fā)送響應體等。Authentication
: 包含了認證成功的用戶的信息,比如用戶名、密碼(通常加密或散列)、權(quán)限等。
設置響應編碼和獲取輸出對象:
httpServletResponse.setContentType("application/json;charset=utf-8");
: 設置響應的內(nèi)容類型為JSON,并指定字符集為UTF-8。PrintWriter writer = httpServletResponse.getWriter();
: 獲取一個PrintWriter
對象,用于向客戶端發(fā)送字符文本數(shù)據(jù)。
構(gòu)建返回的JSON數(shù)據(jù):
- 創(chuàng)建一個
HashMap
來存儲要返回給客戶端的數(shù)據(jù)。 - 從
Authentication
對象中獲取用戶名并添加到map
中。 - 使用Java 8的流(Stream)從
Authentication
對象中獲取用戶的權(quán)限(GrantedAuthority
),并將它們轉(zhuǎn)換為字符串列表,然后添加到map
中。
獲取權(quán)限標識符:
authorities
是一個Collection
類型的集合,它包含了用戶所擁有的權(quán)限信息。每個GrantedAuthority
對象都代表了一個權(quán)限,通常是通過它的getAuthority()
方法來獲取權(quán)限的標識符(通常是一個字符串)。使用 Java 8 的 Stream API,您可以將這個集合轉(zhuǎn)換為一個新的
List
,其中包含了所有權(quán)限的標識符。這是通過以下步驟實現(xiàn)的:
- 調(diào)用
stream()
方法:將Collection
轉(zhuǎn)換為一個Stream
,這樣您就可以使用 Stream API 提供的各種操作了。 - 調(diào)用
map()
方法:map()
方法接受一個函數(shù)作為參數(shù),這個函數(shù)會被應用到 Stream 中的每個元素上。在這個例子中,您傳遞了一個 lambda 表達式item -> item.getAuthority()
,它將每個GrantedAuthority
對象映射為其權(quán)限標識符(即調(diào)用getAuthority()
方法的結(jié)果)。這樣,Stream 中的元素就從GrantedAuthority
對象變成了字符串。 - 調(diào)用
collect()
方法:collect()
方法是一個終端操作,它接受一個Collector
來將 Stream 中的元素累積成一個結(jié)果。在這個例子中,您使用了Collectors.toList()
來收集 Stream 中的所有元素到一個新的List
中。
這行代碼的作用就是:將用戶所擁有的所有權(quán)限(
GrantedAuthority
對象)轉(zhuǎn)換為一個包含這些權(quán)限標識符(字符串)的列表。
構(gòu)建統(tǒng)一的響應對象并轉(zhuǎn)換為JSON字符串:
- 創(chuàng)建一個
R
對象(假設這是一個自定義的響應類,用于封裝響應的狀態(tài)碼、消息和數(shù)據(jù)),將狀態(tài)碼設置為200,消息設置為"登錄成功!",并將JWT令牌作為數(shù)據(jù)設置進去。 - 使用某個JSON庫(如Fastjson、Jackson等)將
R
對象轉(zhuǎn)換為JSON字符串。
發(fā)送響應到客戶端:
- 使用
PrintWriter
將JSON字符串寫入到HTTP響應中。 - 調(diào)用
flush()
方法確保所有緩沖的輸出都被發(fā)送到客戶端。 - 調(diào)用
close()
方法關(guān)閉PrintWriter
。
總的來說,這個
successHandler
方法在用戶成功登錄后,會構(gòu)建一個包含用戶名、權(quán)限和JWT令牌的JSON響應,并將其發(fā)送給客戶端。這樣,客戶端就可以使用這個JWT令牌進行后續(xù)的身份驗證和授權(quán)操作。
1.4 登錄失敗返回的json數(shù)據(jù)
//登錄失敗返回json數(shù)據(jù) private AuthenticationFailureHandler failureHandler(){ return (httpServletRequest, httpServletResponse, e)->{ //設置編碼 httpServletResponse.setContentType("application/json;charset=utf-8"); //獲取輸出對象 PrintWriter writer = httpServletResponse.getWriter(); R r=new R(500,"登錄失??!",e.getMessage()); String s = JSON.toJSONString(r); writer.println(s); writer.flush(); writer.close(); }; }
修改配置
.failureHandler(failureHandler())
1.5 權(quán)限不足返回的json數(shù)據(jù)
//權(quán)限不足返回json數(shù)據(jù) private AccessDeniedHandler accessDeniedHandler(){ return (httpServletRequest, httpServletResponse, e)->{ httpServletResponse.setContentType("application/json;charset=utf-8"); PrintWriter writer = httpServletResponse.getWriter(); R r=new R(403,"權(quán)限不足,請聯(lián)系管理員",e.getMessage()); String s = JSON.toJSONString(r); writer.println(s); writer.flush(); writer.close(); }; }
修改配置
//指定權(quán)限不足跳轉(zhuǎn)的頁面 http.exceptionHandling().accessDeniedHandler(accessDeniedHandler());
1.6 未登錄訪問資源返回json數(shù)據(jù)
需要自定義一個過濾器
@Component //交于spring容器管理 public class LoginFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { //若是登錄路徑,放行 String requestURI = httpServletRequest.getRequestURI(); String method = httpServletRequest.getMethod(); if("/login".equals(requestURI)&&"POST".equals(method)){ //放行 filterChain.doFilter(httpServletRequest,httpServletResponse); return; } //統(tǒng)一編碼格式 httpServletResponse.setContentType("application/json;charset=utf-8"); //1. 從請求頭中獲取token令牌 String token = httpServletRequest.getHeader("token"); //2. 判斷token是否為null if(StringUtils.isEmpty(token)){ //獲取傳輸對象 PrintWriter writer = httpServletResponse.getWriter(); R r =new R(500,"未登錄",null); String s = JSON.toJSONString(r); writer.write(s); writer.flush(); writer.close(); return; } //3. 驗證token if(!JWTUtil.verify(token)){ PrintWriter writer = httpServletResponse.getWriter(); //返回一個token失效的json數(shù)據(jù) R r=new R(500,"token失效!",null); String s = JSON.toJSONString(r); writer.write(s); writer.flush(); writer.close(); return; } //把當前用戶的信息封裝到Authentication對象中 SecurityContext context = SecurityContextHolder.getContext(); Map<String, Object> userInfo = JWTUtil.getInfo(token, "userInfo"); Object username = userInfo.get("username"); //權(quán)限標識符 // List<String> permissions = (List<String>) userInfo.get("permissions"); List<String> permissions = (List<String>) userInfo.get("permission"); //通過stream流轉(zhuǎn)換類型 List<SimpleGrantedAuthority> collect = permissions.stream().map(item -> new SimpleGrantedAuthority(item)).collect(Collectors.toList()); // List<SimpleGrantedAuthority> collect = permissions.stream().map(item -> new SimpleGrantedAuthority(item)).collect(Collectors.toList()); //三個參數(shù) //Object principal,賬號 //Object credentials,密碼 null //Collection<? extends GrantedAuthority> authorities:權(quán)限 UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken=new UsernamePasswordAuthenticationToken(username,null,collect); context.setAuthentication(usernamePasswordAuthenticationToken); //放行 filterChain.doFilter(httpServletRequest,httpServletResponse); } }
用于處理HTTP請求的過濾器方法,通常用于在請求到達控制器之前執(zhí)行一些預處理操作
檢查登錄路徑并放行:
- 首先,方法通過檢查請求的URI和方法來確定是否是一個登錄請求(通常是
/login
路徑且方法為POST
)。 - 如果是登錄請求,則直接調(diào)用
filterChain.doFilter(httpServletRequest, httpServletResponse);
來放行請求,允許它繼續(xù)通過過濾器鏈到達相應的控制器。
從請求頭中獲取Token:
通過
httpServletRequest.getHeader("token")
從HTTP請求頭中獲取名為token
的值,這個值通常是一個JWT(JSON Web Token),用于身份驗證和授權(quán)。
檢查Token是否為空:
- 使用
StringUtils.isEmpty(token)
(這里假設StringUtils
是一個工具類,用于字符串操作)來檢查Token是否為空或null。 - 如果Token為空,則構(gòu)造一個包含錯誤信息的JSON響應(狀態(tài)碼500,消息“未登錄”),并寫入響應體中,然后結(jié)束方法執(zhí)行。
驗證Token:
- 調(diào)用
JWTUtil.verify(token)
(這里假設JWTUtil
是一個工具類,用于處理JWT)來驗證Token的有效性。 - 如果Token無效(例如,簽名不匹配、過期等),則構(gòu)造一個包含錯誤信息的JSON響應(狀態(tài)碼500,消息“token失效!”),并寫入響應體中,然后結(jié)束方法執(zhí)行。
從Token中提取用戶信息并封裝:
- 使用
JWTUtil.getInfo(token, "userInfo")
(這里假設getInfo
方法從Token中提取特定字段的信息,"userInfo"
是字段名)從Token中提取用戶信息。 - 從用戶信息中提取用戶名和權(quán)限列表。注意,這里權(quán)限列表的鍵名從
permissions
更改為permission
,這取決于Token中實際存儲的鍵名。 - 使用Java 8的Stream API將權(quán)限列表中的每個權(quán)限字符串轉(zhuǎn)換為
SimpleGrantedAuthority
對象,這些對象代表了Spring Security中的權(quán)限。
將用戶信息封裝到
Authentication
對象中創(chuàng)建一個
UsernamePasswordAuthenticationToken
(或其他適合的Authentication
子類)實例,設置用戶名、密碼(對于JWT通常不需要,但可以使用null或特殊值)、權(quán)限列表等,并將該實例設置到SecurityContextHolder
中
設置安全上下文:
通過
SecurityContextHolder.getContext().setAuthentication(...)
將身份驗證信息設置到當前線程的安全上下文中。這是必要的,因為Spring Security會在后續(xù)的處理過程中(如訪問控制決策)檢查這個上下文來確定當前用戶的身份和權(quán)限。
修改配置類
在配置類中注入自定義的過濾器
在方法中將自定義的過濾器放在之前
//把自定義的過濾器放在UsernamePasswordAuthenticationFilter之前 http.addFilterBefore(loginFilter, UsernamePasswordAuthenticationFilter.class);
到此這篇關(guān)于使用JWT的SpringSecurity實現(xiàn)前后端分離的文章就介紹到這了,更多相關(guān)SpringSecurity JWT前后端分離內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringSecurity多表多端賬戶登錄的實現(xiàn)
- SpringSecurity集成第三方登錄過程詳解(最新推薦)
- springsecurity實現(xiàn)用戶登錄認證快速使用示例代碼(前后端分離項目)
- SpringSecurity自動登錄流程與實現(xiàn)詳解
- SpringSecurity6自定義JSON登錄的實現(xiàn)
- SpringSecurity6.x多種登錄方式配置小結(jié)
- SpringSecurity+Redis+Jwt實現(xiàn)用戶認證授權(quán)
- SpringSecurity角色權(quán)限控制(SpringBoot+SpringSecurity+JWT)
- SpringBoot3.0+SpringSecurity6.0+JWT的實現(xiàn)
- springSecurity之如何添加自定義過濾器
- springSecurity自定義登錄接口和JWT認證過濾器的流程
相關(guān)文章
Java17中record替代Lombok部分功能使用場景探究
這篇文章主要介紹了使用Java17中的record替代Lombok的部分功能,本文來為大家小小的總結(jié)下,我們可以在哪些地方,利用record來替換Lombok2024-01-01Springboot集成JUnit5優(yōu)雅進行單元測試的示例
這篇文章主要介紹了Springboot集成JUnit5優(yōu)雅進行單元測試的示例,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下2020-10-10Spring Security如何在Servlet中執(zhí)行
這篇文章主要介紹了Spring Security如何在Servlet中執(zhí)行,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-04-04關(guān)于java.io.EOFException產(chǎn)生的原因以及解決方案
文章總結(jié):EOFException異常通常發(fā)生在嘗試從空的ObjectInputStream對象中讀取數(shù)據(jù)時,解決方法是在finally語句中添加判斷,確保objectInputStream不為空后再進行關(guān)閉操作,在處理1.txt文件為空的情況時,捕獲EOFException可以避免程序終止,并且不會拋出空指針異常2025-01-01