Spring?Security+JWT如何實(shí)現(xiàn)前后端分離權(quán)限控制
Spring Security+JWT實(shí)現(xiàn)前后端分離權(quán)限控制實(shí)戰(zhàn)
在前后端分離項(xiàng)目中,傳統(tǒng)的基于 Session 的認(rèn)證方式已不再適用。取而代之的是更加輕量、高效的 JWT(JSON Web Token)方式來實(shí)現(xiàn)無狀態(tài)認(rèn)證。
一、為什么要用 JWT?
前后端分離架構(gòu)的挑戰(zhàn):
- 無法使用 Session 管理登錄狀態(tài)(前端和后端分離、跨域)
- 需要一種「無狀態(tài)認(rèn)證機(jī)制」
JWT 的優(yōu)勢(shì):
- 無需在服務(wù)端存儲(chǔ)會(huì)話信息(Token 自包含)
- 結(jié)構(gòu)清晰,支持權(quán)限聲明(claims)
- 可擴(kuò)展性強(qiáng),可用于 OAuth、SSO 等場(chǎng)景
二、JWT 基本結(jié)構(gòu)
一個(gè) JWT Token 一般分為三段:
Header.Payload.Signature
例如:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyIiwiaWF0IjoxNjg3NjQ5fQ.K4KgD1sE0JQzA1K6k-FaSd56fQ
每部分作用:
| 部分 | 內(nèi)容 |
|---|---|
| Header | 簽名算法,如 HS256 |
| Payload | 載荷(如用戶名、角色、過期時(shí)間) |
| Signature | 簽名(防止篡改) |
三、集成 JWT 的 Spring Security 權(quán)限控制思路
整體流程如下:
前端登錄 -> 后端驗(yàn)證用戶 -> 生成 JWT -> 返回給前端 -> 前端每次請(qǐng)求攜帶 Token -> 后端解析驗(yàn)證并授權(quán)
四、核心模塊代碼實(shí)戰(zhàn)
1. 引入依賴(Spring Boot 3.x 示例)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>2. JWT 工具類
@Component
public class JwtUtil {
private final String SECRET = "MyJwtSecretKey123"; // 建議放到配置文件中
private final long EXPIRATION = 1000 * 60 * 60; // 1小時(shí)
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
.signWith(SignatureAlgorithm.HS256, SECRET)
.compact();
}
public String getUsernameFromToken(String token) {
return Jwts.parser().setSigningKey(SECRET)
.parseClaimsJws(token).getBody().getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
}3. 自定義登錄接口生成 Token
@RestController
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtUtil jwtUtil;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
try {
Authentication auth = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())
);
String token = jwtUtil.generateToken(loginRequest.getUsername());
return ResponseEntity.ok(Collections.singletonMap("token", token));
} catch (AuthenticationException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("用戶名或密碼錯(cuò)誤");
}
}
}4. JWT 認(rèn)證過濾器
@Component
public class JwtAuthFilter extends OncePerRequestFilter {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
String username = jwtUtil.getUsernameFromToken(token);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(token)) {
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
}
chain.doFilter(request, response);
}
}5. 配置 SecurityFilterChain
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
@Autowired
private JwtAuthFilter jwtAuthFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/login").permitAll()
.anyRequest().authenticated()
)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
}五、前端如何配合使用?
- 登錄后保存返回的
token - 所有后續(xù)請(qǐng)求在 header 中添加:
Authorization: Bearer <你的 token>
六、權(quán)限控制示例
@RestController
public class UserController {
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/admin/data")
public String adminData() {
return "管理員數(shù)據(jù)";
}
@PreAuthorize("hasAnyRole('USER','ADMIN')")
@GetMapping("/user/data")
public String userData() {
return "用戶數(shù)據(jù)";
}
}總結(jié)
JWT 與 Spring Security 的結(jié)合,可以幫助你構(gòu)建一個(gè)無狀態(tài)、安全、高效的前后端分離權(quán)限系統(tǒng)。它簡(jiǎn)化了登錄狀態(tài)的管理流程,提高了系統(tǒng)的伸縮性與并發(fā)處理能力。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- vue3整合SpringSecurity加JWT實(shí)現(xiàn)登錄認(rèn)證
- SpringSecurity JWT基于令牌的無狀態(tài)認(rèn)證實(shí)現(xiàn)
- SpringSecurity6.0 如何通過JWTtoken進(jìn)行認(rèn)證授權(quán)
- springboot?security使用jwt認(rèn)證方式
- SpringBoot3.x接入Security6.x實(shí)現(xiàn)JWT認(rèn)證的完整步驟
- Spring?Clou整合?Security?+?Oauth2?+?jwt實(shí)現(xiàn)權(quán)限認(rèn)證的詳細(xì)過程
相關(guān)文章
springboot配置多數(shù)據(jù)源的一款框架(dynamic-datasource-spring-boot-starter
dynamic-datasource-spring-boot-starter 是一個(gè)基于 springboot 的快速集成多數(shù)據(jù)源的啟動(dòng)器,今天通過本文給大家分享這款框架配置springboot多數(shù)據(jù)源的方法,一起看看吧2021-09-09
Java8中orElse orElseGet orElseThrow的使用舉例
這篇文章主要介紹了Java8中orElse orElseGet orElseThrow的使用舉例,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2025-09-09
Maven編譯Fatal?error?compiling:無效的目標(biāo)發(fā)行版:11問題及解決
在Java11中編譯Springboot工程時(shí)遇到問題,解決方法是在pom.xml文件中指定Maven的Java編譯器版本,可以使用MavenJava編譯器屬性或插件,在Java9及以后的版本中,也要使用插件并設(shè)置release屬性2024-12-12
IO流概述分類字節(jié)流寫數(shù)據(jù)三種方式及問題分析
這篇文章主要為大家介紹了IO流概述分類字節(jié)流寫數(shù)據(jù)三種方式及寫數(shù)據(jù)問題分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
SpringBoot接口訪問頻率限制的實(shí)現(xiàn)方式
接口訪問頻率限制是通過在一定時(shí)間內(nèi)限制用戶對(duì)接口的訪問次數(shù)來實(shí)現(xiàn)的,在Spring Boot中,我們可以通過多種方式來實(shí)現(xiàn)接口的限流,如使用過濾器、攔截器或者借助第三方庫,本文給大家講解的非常詳細(xì),需要的朋友可以參考下2024-07-07
mybatis中association標(biāo)簽的使用解讀
這篇文章主要介紹了mybatis中association標(biāo)簽的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
Spring Gateway自定義請(qǐng)求參數(shù)封裝的實(shí)現(xiàn)示例
這篇文章主要介紹了Spring Gateway自定義請(qǐng)求參數(shù)封裝的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

