詳解Java?token主流框架之JWT
1. JWT的概念和特點
JWT是一種輕量級、可擴展、可自包含的身份驗證和授權(quán)機制。它是由三個部分組成:頭部(Header)、載荷(Payload)和簽名(Signature)。它的目的是為了在網(wǎng)絡(luò)應(yīng)用間傳遞聲明信息,以便在某些情況下對用戶進行身份驗證和授權(quán)。
JWT有以下幾個特點:
- 簡潔(Compact):JWT是一個緊湊且自包含的數(shù)據(jù)格式,它可以通過HTTP頭或URL參數(shù)進行傳輸。
- 自包含(Self-contained):JWT包含了足夠的信息,使得客戶端可以判斷是否信任該令牌,而不需要查詢服務(wù)器。
- 可擴展(Extensible):由于JWT是基于JSON格式的,因此可以自定義Payload中的屬性來適應(yīng)各種業(yè)務(wù)需求。
- 安全(Secure):JWT使用簽名來驗證發(fā)送方和接收方之間的身份。當使用加密算法時,還可以確保消息的機密性。
2. JWT的三個部分:Header、Payload和Signature
JWT由三個部分組成:頭部、載荷和簽名。
Header
JWT頭部是一個JSON對象,它描述了生成和處理該JWT所需要的算法和類型信息。例如:
{ "alg": "HS256", "typ": "JWT" }
其中,"alg"表示簽名算法,"typ"表示令牌類型。
Payload
JWT載荷是一個JSON對象,它包含了有關(guān)令牌和聲明的信息。例如:
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }
其中,"sub"表示主題(Subject),"name"表示名稱,"iat"表示令牌的發(fā)行時間(Issued At)。
除了這些聲明外,JWT還支持自定義聲明。例如:
{ "iss": "http://example.com", "exp": 1516239922, "custom_key": "custom_value" }
其中,"iss"表示頒發(fā)者(Issuer),"exp"表示到期時間(Expiration Time),"custom_key"是自定義聲明。
Signature
JWT簽名用于驗證消息的發(fā)送方和消息的完整性。簽名由頭部、載荷和密鑰組成,并使用指定的算法進行計算。例如,使用HS256算法可以使用以下方法生成簽名:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
其中,"."是分隔符,"secret"是密鑰。
3. JWT的工作過程
JWT的工作流程可以描述為以下三個步驟:
- 用戶通過用戶名和密碼進行身份驗證。
- 服務(wù)器對用戶進行身份驗證,并創(chuàng)建一個JWT令牌,并將其返回給客戶端。
- 客戶端在以后的請求中使用該令牌進行身份驗證,并在需要訪問API時將其發(fā)送到服務(wù)器。
具體的工作過程如下:
- 當用戶成功登錄到Web應(yīng)用程序時,服務(wù)器將根據(jù)用戶提供的憑據(jù)(例如用戶名和密碼)驗證用戶的身份,并創(chuàng)建一個JWT令牌。
- 服務(wù)器將從Payload中提取一些信息(例如用戶ID或名稱)并將其加密到JWT令牌中。
- JWT令牌將被加密,并在響應(yīng)頭或URL參數(shù)中返回給客戶端。
- 在以后的請求中,客戶端將JWT令牌作為授權(quán)標頭或URL參數(shù)發(fā)送給服務(wù)器。
- 服務(wù)器將使用該令牌進行身份驗證,并檢查令牌是否被篡改或過期。如果令牌有效,則允許訪問所需的API。
4. JWT常見的應(yīng)用場景和優(yōu)勢
JWT主要應(yīng)用于Web應(yīng)用程序和RESTful API,以下是它的一些常見應(yīng)用場景:
- 身份驗證(Authentication):JWT可以作為身份驗證機制,代替?zhèn)鹘y(tǒng)的Cookie和Session方式。
- 前后端分離(Front and Backend Separation):在前后端分離的架構(gòu)中,JWT可以在前端和后端之間進行信任關(guān)系的建立和維護。
- 單點登錄(Single Sign On):在多個Web應(yīng)用程序中共享JWT,可以實現(xiàn)單點登錄的效果。
- 信息交換(Information Exchange):JWT可以用于安全地在不同的系統(tǒng)之間傳遞信息。
JWT的優(yōu)勢包括:
- 無狀態(tài)(Stateless): JWT不需要在服務(wù)器上保存用戶狀態(tài)和會話信息,從而簡化了服務(wù)器的負載和擴展性。
- 可擴展(Extensible):JWT的載荷可以包含自定義聲明,從而滿足各種業(yè)務(wù)場景的需求。
- 安全(Secure):JWT使用簽名來驗證發(fā)送方和接收方之間的身份。當使用加密算法時,還可以確保消息的機密性。
- 支持跨域(Cross-origin):JWT可以通過HTTP頭或URL參數(shù)進行傳輸,因此支持跨域訪問。
5. 如何避免JWT的安全風險
使用JWT時,需要注意以下幾點來避免安全風險:
- 不要將敏感信息存儲在JWT中:JWT可以被解密,因此不應(yīng)該將任何敏感信息存儲在JWT中。
- 對于重要的操作,需要再次進行身份驗證:JWT只是一種身份驗證機制,而不是授權(quán)機制。即使用戶已經(jīng)通過JWT進行了身份驗證,服務(wù)器仍然需要對他們進行授權(quán)和驗證。
- 使用HTTPS協(xié)議:由于JWT可能被篡改,因此需要確保使用HTTPS協(xié)議以確保消息的機密性和完整性。
- 設(shè)置合理的過期時間:如果JWT永遠不會過期,那么它就會成為一個長期有效的訪問令牌,從而增加了訪問令牌被攻擊者盜用的風險。
6.代碼案例
- JJWT(Java JWT)
JJWT 是一個流行的 Java JWT 庫,它提供了一種創(chuàng)建、編碼和解碼 JWT 的簡便方法。以下是一個基于 JJWT 的示例:
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; public class JwtUtils { private static final long EXPIRATION_TIME = 864_000_000; // 10 days private static final String SECRET_KEY = "secretKey"; public static String generateToken(String username) { Date expiryDate = new Date(System.currentTimeMillis() + EXPIRATION_TIME); return Jwts.builder() .setSubject(username) .setExpiration(expiryDate) .signWith(SignatureAlgorithm.HS512, SECRET_KEY) .compact(); } public static String getUsernameFromToken(String token) { Claims claims = Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody(); return claims.getSubject(); } public static boolean isTokenValid(String token) { try { Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token); return true; } catch (Exception e) { return false; } } }
在這個示例中,JwtUtils
類包含了生成令牌、從令牌中獲取用戶名和驗證令牌的三個方法。注意,在實際使用中,SECRET_KEY
應(yīng)該更加復(fù)雜。
- Spring Security
Spring Security 是一個流行的安全框架,它提供了一種輕松的方式來保護應(yīng)用程序和 APIs。Spring Security 支持使用 JWT 進行身份驗證和授權(quán)。以下是一個基于 Spring Security 的示例:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() .antMatchers("/api/auth/**").permitAll() .anyRequest().authenticated() .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } @Bean public JwtAuthenticationFilter jwtAuthenticationFilter() { return new JwtAuthenticationFilter(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
在這個示例中,SecurityConfig
類擴展了 WebSecurityConfigurerAdapter
類,并覆蓋了其中的 configure(HttpSecurity http)
方法。在這個方法中,Spring Security 配置了哪些 URL 公開,哪些需要進行身份驗證,并且禁用了 CSRF 保護。配置完后,Spring Security 將 JWT 過濾器添加到 UsernamePasswordAuthenticationFilter
前面。
在實現(xiàn)身份驗證之前,需要創(chuàng)建一個用戶服務(wù)類并實現(xiàn) UserDetailsService
接口。以下是一個示例:
@Service public class UserService implements UserDetailsService { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("User not found")); return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), new ArrayList<>()); } }
在這個示例中,UserService
類實現(xiàn)了 UserDetailsService
接口,并覆蓋其中的 loadUserByUsername(String username)
方法。該方法根據(jù)給定的用戶名查找用戶,如果用戶不存在,則拋出 UsernameNotFoundException
異常。
最后,需要實現(xiàn)一個 JWT 過濾器來驗證 JWT 并將身份驗證信息加載到 Spring Security 的上下文中。以下是一個基于 Spring Security 的 JWT 過濾器示例:
public class JwtAuthenticationFilter extends OncePerRequestFilter { private static final String AUTHORIZATION_HEADER = "Authorization"; private static final String TOKEN_PREFIX = "Bearer "; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { String token = parseToken(request); if (token != null && JwtUtils.isTokenValid(token)) { String username = JwtUtils.getUsernameFromToken(token); UserDetails userDetails = userService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } } catch (Exception e) { logger.error("Error while processing authentication request", e); } filterChain.doFilter(request, response); } private String parseToken(HttpServletRequest request) { String header = request.getHeader(AUTHORIZATION_HEADER); if (header != null && header.startsWith(TOKEN_PREFIX)) { return header.substring(TOKEN_PREFIX.length()); } return null; } }
在這個示例中,JWT 過濾器實現(xiàn)了 OncePerRequestFilter
類,并覆蓋了其中的 doFilterInternal
方法。在該方法中,它從請求頭中獲取 JWT,驗證 JWT,將身份驗證信息加載到 Spring Security 的上下文中,然后放行請求。
- Apache Shiro
Apache Shiro 是一個功能豐富的安全框架,可以用于保護 Java 應(yīng)用程序和 APIs。Shiro 支持使用 JWT 進行身份驗證和授權(quán)。以下是一個基于 Shiro 的示例:
@Configuration public class ShiroConfig { @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm()); return securityManager; } @Bean public JwtRealm realm() { JwtRealm realm = new JwtRealm(); realm.setCredentialsMatcher(jwtCredentialsMatcher()); return realm; } @Bean public JwtCredentialsMatcher jwtCredentialsMatcher() { return new JwtCredentialsMatcher(); } @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean(); filterFactoryBean.setSecurityManager(securityManager); filterFactoryBean.setFilters(filters()); filterFactoryBean.setFilterChainDefinitionMap(filterChainDefinition()); return filterFactoryBean; } private Map<String, String> filterChainDefinition() { Map<String, String> filterChainDefinition = new LinkedHashMap<>(); filterChainDefinition.put("/api/auth/**", "anon"); filterChainDefinition.put("/**", "jwt"); return filterChainDefinition; } private Map<String, Filter> filters() { Map<String, Filter> filters = new HashMap<>(); filters.put("jwt", new JwtFilter()); return filters; } }
在這個示例中,Shiro 配置了一個名為 securityManager
的安全管理器,其中包含一個名為 realm
的域。realm
類需要實現(xiàn) org.apache.shiro.realm.Realm
接口,并覆蓋其中的 supports(AuthenticationToken token)
和 getAuthenticationInfo(AuthenticationToken token)
方法來驗證 JWT。
類似于 Spring Security,需要實現(xiàn)一個 JWT 過濾器來驗證 JWT 并將身份驗證信息加載到 Shiro 的上下文中。以下是一個基于 Shiro 的 JWT 過濾器示例:
public class JwtFilter extends AuthenticatingFilter { @Override protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) { String token = getToken(request); if (StringUtils.isNotBlank(token) && JwtUtils.isTokenValid(token)) { return new JwtToken(token); } return null; } @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { HttpServletResponse httpServletResponse = (HttpServletResponse) response; httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value()); return false; } private String getToken(ServletRequest request) { HttpServletRequest httpServletRequest = (HttpServletRequest) request; String token = httpServletRequest.getHeader("Authorization"); if (StringUtils.isNotBlank(token) && token.startsWith("Bearer ")) { return token.substring(7); } else { return null; } } }
在這個示例中,JWT 過濾器擴展了 AuthenticatingFilter
類,并覆蓋了其中的 createToken(ServletRequest request, ServletResponse response)
和 onAccessDenied(ServletRequest request, ServletResponse response)
方法。createToken
方法創(chuàng)建一個 JwtToken 對象,并將 JWT 設(shè)置為憑據(jù)。onAccessDenied
方法在未找到 JWT 時返回 UNAUTHORIZED 狀態(tài)碼。
以上就是詳解Java token主流框架之JWT的詳細內(nèi)容,更多關(guān)于Java token框架JWT的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java實現(xiàn)JSON與XML相互轉(zhuǎn)換的簡明教程
Java實現(xiàn)復(fù)雜數(shù)據(jù)結(jié)構(gòu)(如嵌套對象、數(shù)組)在 JSON 與 XML 之間的相互轉(zhuǎn)換,可以使用 Jackson 和 Jackson XML 擴展庫來完成,Jackson 是一個流行的 JSON 處理庫,通過 Jackson 的 XML 擴展庫,可以實現(xiàn) JSON 和 XML 之間的轉(zhuǎn)換,需要的朋友可以參考下2024-08-08java 數(shù)據(jù)結(jié)構(gòu) 冒泡排序?qū)崿F(xiàn)代碼
這篇文章主要介紹了java 數(shù)據(jù)結(jié)構(gòu) 冒泡排序的相關(guān)資料,并附實例代碼,有需要的小伙伴可以參考下2016-09-09java線程安全鎖ReentrantReadWriteLock原理分析readLock
這篇文章主要為大家介紹了java線程安全鎖ReentrantReadWriteLock原理分析readLock,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10