SpringSecurity?鑒權與授權的具體使用
前言
在現(xiàn)代 Web 開發(fā)中,前后端分離架構已經(jīng)成為主流。后端專注于提供 RESTful API,而前端通過 AJAX 請求與后端交互。在這種架構下,如何對用戶進行 認證(Authentication) 和 授權(Authorization) 成為了系統(tǒng)設計中的核心問題。
Spring Security 是 Spring 框架中用于構建安全系統(tǒng)的模塊,它不僅提供了強大的安全機制,還支持靈活的自定義配置。本文將圍繞 鑒權失敗和成功時的行為、需要攔截的路徑配置、以及具體的代碼實現(xiàn)方式 展開講解,并結合多個實際例子,幫助你深入理解 Spring Security 在前后端分離項目中的使用。
本文將從以下三個方面詳細展開:
- 基礎概念:包括認證、授權、關鍵組件及其作用;
- 配置流程:從登錄請求到權限控制的完整流程;
- 實戰(zhàn)示例:JWT 認證、異常處理、多種路徑保護等具體實現(xiàn);
無論你是初學者還是有一定經(jīng)驗的開發(fā)者,這篇文章都將為你提供一套完整的解決方案。
一、基礎概念詳解
Spring Security 的核心在于 認證(Authentication) 和 授權(Authorization)。
1.SecurityFilterChain(安全過濾器鏈)
- 作用:定義 HTTP 請求的安全策略。
- 位置:通常在配置類中通過
@Bean注解創(chuàng)建。 - 示例:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(withDefaults())
.httpBasic(withDefaults());
return http.build();
}
可以定義多個 SecurityFilterChain 來處理不同路徑的權限策略。
2.UserDetailsService(用戶詳情服務)
- 作用:負責加載用戶信息(用戶名、密碼、權限)。
- 實現(xiàn)方式:
- 內存方式(適合測試):
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withUsername("user")
.password(passwordEncoder().encode("123456"))
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
- 數(shù)據(jù)庫方式(推薦生產環(huán)境):
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("用戶不存在"));
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
getAuthorities(user.getRoles()));
}
private Collection<? extends GrantedAuthority> getAuthorities(Collection<Role> roles) {
return roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName()))
.collect(Collectors.toList());
}
}
3.PasswordEncoder(密碼編碼器)
- 作用:加密存儲密碼,并驗證密碼是否匹配。
- 常用實現(xiàn):
BCryptPasswordEncoder(推薦)NoOpPasswordEncoder(不加密,僅用于開發(fā)階段)
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
4.JwtRequestFilter(自定義 JWT 過濾器)
- 作用:攔截每個請求,解析 Token 并設置當前用戶認證信息。
- 實現(xiàn)方式:
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String token = extractToken(request);
if (token != null && jwtTokenUtil.validateToken(token)) {
String username = jwtTokenUtil.getUsernameFromToken(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
private String extractToken(HttpServletRequest request) {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
return header.substring(7);
}
return null;
}
}
5.AuthenticationEntryPoint(鑒權失敗處理器)
- 作用:當用戶未認證訪問受保護資源時觸發(fā)該處理器。
- 實現(xiàn)方式:
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("{\"error\": \"Unauthorized\", \"message\": \"認證失敗,請重新登錄\"}");
}
}
6.AccessDeniedHandler(權限不足處理器)
- 作用:當用戶已認證但沒有訪問權限時觸發(fā)。
- 實現(xiàn)方式:
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException {
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.getWriter().write("{\"error\": \"Forbidden\", \"message\": \"你沒有權限訪問該資源\"}");
}
}
二、配置流程詳解
成功時的流程:
- 用戶發(fā)送登錄請求 →
/api/auth/login - 后端驗證用戶名密碼,生成 JWT Token 并返回給前端
- 前端保存 Token(如 localStorage)
- 后續(xù)請求攜帶 Token(放在 Header 中)
- 后端通過自定義 JWT 過濾器解析 Token 并設置認證信息
- 用戶訪問受保護資源成功
失敗時的流程:
- Token 缺失或格式錯誤 → 返回
401 Unauthorized - Token 已過期或簽名無效 → 返回
401 Unauthorized - 用戶沒有權限訪問某資源 → 返回
403 Forbidden
三、實戰(zhàn)示例詳解
示例 1:JWT Token 工具類(完整版)
@Component
public class JwtTokenUtil {
private static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60; // 5小時
private String secret = "your-secret-key-here";
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
public Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
}
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
private Claims getAllClaimsFromToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(secret)
.build()
.parseClaimsJws(token)
.getBody();
}
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return doGenerateToken(claims, userDetails.getUsername());
}
private String doGenerateToken(Map<String, Object> claims, String subject) {
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}
示例 2:登錄接口
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) throws Exception {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())
);
} catch (AuthenticationException e) {
throw new Exception("用戶名或密碼錯誤");
}
final UserDetails userDetails = userDetailsService.loadUserByUsername(loginRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok().header("Authorization", "Bearer " + token).build();
}
}
示例 3:多種路徑保護方式
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(exceptions -> exceptions
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
);
return http.build();
}
結語
本文從 Spring Security 的基礎概念出發(fā),詳細介紹了認證與授權的核心組件,并結合多個實戰(zhàn)示例展示了 JWT 的集成、路徑保護、異常處理等常見場景的實現(xiàn)方式。
到此這篇關于SpringSecurity 鑒權與授權的具體使用的文章就介紹到這了,更多相關SpringSecurity 鑒權與授權內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- Springboot整合SpringSecurity實現(xiàn)登錄認證和鑒權全過程
- SpringBoot整合SpringSecurity和JWT和Redis實現(xiàn)統(tǒng)一鑒權認證
- SpringBoot整合SpringSecurityOauth2實現(xiàn)鑒權動態(tài)權限問題
- SpringBoot集成SpringSecurity和JWT做登陸鑒權的實現(xiàn)
- SpringSecurity動態(tài)加載用戶角色權限實現(xiàn)登錄及鑒權功能
- springboot+jwt+springSecurity微信小程序授權登錄問題
- SpringSecurity實現(xiàn)權限認證與授權的使用示例
- SpringSecurity進行認證與授權的示例代碼
- springSecurity用戶認證和授權的實現(xiàn)
- 深入淺析springsecurity入門登錄授權
- mall整合SpringSecurity及JWT實現(xiàn)認證授權實戰(zhàn)
- SpringSecurity頁面授權與登錄驗證實現(xiàn)(內存取值與數(shù)據(jù)庫取值)
相關文章
SpringBoot整合達夢數(shù)據(jù)庫的教程詳解
這篇文章主要給大家介紹了SpringBoot整合達夢數(shù)據(jù)庫的詳細教程,文章中有詳細的圖片介紹和代碼示例供大家參考,具有一定的參考價值,需要的朋友可以參考下2023-08-08
解析java稀疏數(shù)組如何幫助我們節(jié)省內存提升性能
這篇文章主要為大家介紹了java稀疏數(shù)組如何幫助我們節(jié)省內存提升性能解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11

