做视频网站需要什么服务器配置,wordpress老文章,网页设计美工培训,酒店网站的建设方案使用 WebSecurityConfigurationAdapter
在前后端分离的架构中#xff0c;通常使用 Token 进行认证和授权是一种常见的做法。Token 可以是 JSON Web Token#xff08;JWT#xff09;#xff0c;用于在客户端和服务器之间传递身份信息和访问控制信息。下面我将详细介绍如何在…使用 WebSecurityConfigurationAdapter
在前后端分离的架构中通常使用 Token 进行认证和授权是一种常见的做法。Token 可以是 JSON Web TokenJWT用于在客户端和服务器之间传递身份信息和访问控制信息。下面我将详细介绍如何在 Spring Boot 后端和 Vue 前端应用中使用 TokenJWT来实现认证和授权。
后端Spring Boot Spring Security JWT
1. 添加依赖
首先确保在你的 Spring Boot 项目中添加相关依赖
!-- Spring Security --
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId
/dependency!-- JWT --
dependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt/artifactIdversion0.9.1/version
/dependency2. 配置Spring Security和JWT
创建一个配置类来配置 Spring Security 和 JWT。
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;Configuration
EnableWebSecurity
EnableGlobalMethodSecurity(prePostEnabled true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {Autowiredprivate UserDetailsService userDetailsService;Autowiredprivate JwtRequestFilter jwtRequestFilter;Autowiredprivate CustomAuthenticationEntryPoint customAuthenticationEntryPoint;Autowiredprivate CustomAccessDeniedHandler customAccessDeniedHandler;Autowiredpublic void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());}Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests().antMatchers(/api/public/**).permitAll() // 公开访问的API.anyRequest().authenticated().and().exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint).accessDeniedHandler(customAccessDeniedHandler).and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 禁用Spring Security的Session管理http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);}Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}Beanpublic JwtTokenUtil jwtTokenUtil() {return new JwtTokenUtil();}BeanOverridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}
}3. JWT Token Util 类
创建一个用于生成和验证 JWT 的工具类 JwtTokenUtil。
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;Component
public class JwtTokenUtil implements Serializable {Value(${jwt.secret})private String secret;Value(${jwt.expiration})private long expiration;public String generateToken(UserDetails userDetails) {MapString, Object claims new HashMap();return Jwts.builder().setClaims(claims).setSubject(userDetails.getUsername()).setIssuedAt(new Date(System.currentTimeMillis())).setExpiration(new Date(System.currentTimeMillis() expiration * 1000)).signWith(SignatureAlgorithm.HS512, secret).compact();}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, FunctionClaims, T claimsResolver) {final Claims claims getAllClaimsFromToken(token);return claimsResolver.apply(claims);}private Claims getAllClaimsFromToken(String token) {return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();}public boolean validateToken(String token, UserDetails userDetails) {final String username getUsernameFromToken(token);return (username.equals(userDetails.getUsername()) !isTokenExpired(token));}private boolean isTokenExpired(String token) {final Date expiration getExpirationDateFromToken(token);return expiration.before(new Date());}
}4. JWT 请求过滤器
创建一个 JWT 请求过滤器来拦截和验证请求中的 JWT。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;Component
public class JwtRequestFilter extends OncePerRequestFilter {Autowiredprivate JwtTokenUtil jwtTokenUtil;Autowiredprivate UserDetailsService userDetailsService;Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws ServletException, IOException {final String authorizationHeader request.getHeader(Authorization);String username null;String jwt null;if (authorizationHeader ! null authorizationHeader.startsWith(Bearer )) {jwt authorizationHeader.substring(7);username jwtTokenUtil.getUsernameFromToken(jwt);}if (username ! null SecurityContextHolder.getContext().getAuthentication() null) {UserDetails userDetails this.userDetailsService.loadUserByUsername(username);if (jwtTokenUtil.validateToken(jwt, userDetails)) {UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);}}chain.doFilter(request, response);}
}5. 登录和生成JWT
创建一个登录接口验证用户身份并生成JWT返回给客户端。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;RestController
public class JwtAuthenticationController {Autowiredprivate AuthenticationManager authenticationManager;Autowiredprivate JwtTokenUtil jwtTokenUtil;Autowiredprivate UserDetailsService userDetailsService;PostMapping(value /api/login)public ResponseEntity? createAuthenticationToken(RequestBody JwtRequest authenticationRequest) throws Exception {authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());final UserDetails userDetails userDetailsService.loadUserByUsername(authenticationRequest.getUsername());final String token jwtTokenUtil.generateToken(userDetails);return ResponseEntity.ok(new JwtResponse(token));}private void authenticate(String username, String password) throws Exception {try {authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));} catch (DisabledException e) {throw new Exception(USER_DISABLED, e);} catch (BadCredentialsException e) {throw new Exception(INVALID_CREDENTIALS, e);}}
}前端Vue
在前端Vue应用中你需要处理JWT的存储和使用。
1. 登录请求
在Vue组件中实现用户登录请求获取JWT并存储到LocalStorage。
// 简化的登录方法示例
login(username, password) {return axios.post(/api/login, { username, password }).then(response {if (response.data response.data.token) {// 将JWT Token保存到LocalStorage中localStorage.setItem(jwtToken, response.data.token);}return response.data;});
}2. JWT Token的存储和使用
在Vue应用中通常将JWT Token存储在LocalStorage中并在每次请求时将Token添加到请求的Header中以便后端验证用户的身份和权限。
// 设置全局的axios默认请求头添加Authorization字段
axios.defaults.headers.common[Authorization] Bearer localStorage.getItem(jwtToken);// 示例获取用户信息的方法
getUserInfo() {return axios.get(/api/userinfo).then(response {return response.data;});
}// 示例注销方法
logout() {localStorage.removeItem(jwtToken); // 清除本地存储的JWT Token// 可以选择发送注销请求到后端使后端的Token失效
}3. 路由守卫和权限控制
使用Vue Router的导航守卫路由守卫根据用户的登录状态和权限信息控制页面的访问。
import Vue from vue
import Router from vue-router
import Home from ./views/Home.vue
import AdminPanel from ./views/AdminPanel.vue
import Login from ./views/Login.vueVue.use(Router)const router new Router({routes: [{path: /,name: home,component: Home,meta: { requiresAuth: true } // 需要登录才能访问的页面},{path: /admin,name: admin,component: AdminPanel,meta: { requiresAuth: true, requiresAdmin: true } // 需要管理员权限才能访问的页面},{path: /login,name: login,component: Login}]
});router.beforeEach((to, from, next) {const jwtToken localStorage.getItem(jwtToken);if (to.matched.some(record record.meta.requiresAuth)) {if (!jwtToken) {next(/login); // 没有登录跳转到登录页面} else {// 验证Token是否过期等逻辑可以根据实际需求添加next();}} else {next(); // 不需要登录的页面直接放行}
});export default router;4. 处理Token过期和刷新
在使用JWT时需要处理Token过期的情况一般的做法是在前端捕获HTTP请求返回的401状态码未授权然后根据情况重新获取新的Token。
// 示例处理HTTP请求返回的401状态码
axios.interceptors.response.use(response {return response;},error {if (error.response.status 401) {// 清除本地存储的TokenlocalStorage.removeItem(jwtToken);// 跳转到登录页面或者重新获取新的Token等操作// 可以根据实际情况进行处理}return Promise.reject(error);}
);使用 SecurityFilterChain
在最新版本的 Spring Security 中WebSecurityConfigurerAdapter 已经被弃用。取而代之的是新的配置方式直接通过配置类和 SecurityFilterChain Bean 来配置安全性。下面是如何在不使用 WebSecurityConfigurerAdapter 的情况下配置 Spring Security 和 JWT 认证。
后端Spring Boot Spring Security JWT
1. 添加依赖
首先确保在你的 Spring Boot 项目中添加相关依赖
!-- Spring Security --
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId
/dependency!-- JWT --
dependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt/artifactIdversion0.9.1/version
/dependency2. 配置 Spring Security 和 JWT
不使用 WebSecurityConfigurerAdapter 的情况下可以使用 SecurityConfigurer 和 SecurityFilterChain 进行配置。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;Configuration
EnableWebSecurity
EnableGlobalMethodSecurity(prePostEnabled true)
public class SecurityConfig {private final JwtRequestFilter jwtRequestFilter;private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint;private final CustomAccessDeniedHandler customAccessDeniedHandler;public SecurityConfig(JwtRequestFilter jwtRequestFilter,CustomAuthenticationEntryPoint customAuthenticationEntryPoint,CustomAccessDeniedHandler customAccessDeniedHandler) {this.jwtRequestFilter jwtRequestFilter;this.customAuthenticationEntryPoint customAuthenticationEntryPoint;this.customAccessDeniedHandler customAccessDeniedHandler;}Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.csrf(AbstractHttpConfigurer::disable).authorizeRequests(authorizeRequests -authorizeRequests.antMatchers(/api/public/**).permitAll().anyRequest().authenticated()).exceptionHandling(exceptionHandling -exceptionHandling.authenticationEntryPoint(customAuthenticationEntryPoint).accessDeniedHandler(customAccessDeniedHandler)).sessionManagement(sessionManagement -sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)).addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);return http.build();}Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {return authenticationConfiguration.getAuthenticationManager();}
}3. JWT Token Util 类
创建一个用于生成和验证 JWT 的工具类 JwtTokenUtil。
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;Component
public class JwtTokenUtil implements Serializable {Value(${jwt.secret})private String secret;Value(${jwt.expiration})private long expiration;public String generateToken(UserDetails userDetails) {MapString, Object claims new HashMap();return Jwts.builder().setClaims(claims).setSubject(userDetails.getUsername()).setIssuedAt(new Date(System.currentTimeMillis())).setExpiration(new Date(System.currentTimeMillis() expiration * 1000)).signWith(SignatureAlgorithm.HS512, secret).compact();}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, FunctionClaims, T claimsResolver) {final Claims claims getAllClaimsFromToken(token);return claimsResolver.apply(claims);}private Claims getAllClaimsFromToken(String token) {return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();}public boolean validateToken(String token, UserDetails userDetails) {final String username getUsernameFromToken(token);return (username.equals(userDetails.getUsername()) !isTokenExpired(token));}private boolean isTokenExpired(String token) {final Date expiration getExpirationDateFromToken(token);return expiration.before(new Date());}
}4. JWT 请求过滤器
创建一个 JWT 请求过滤器来拦截和验证请求中的 JWT。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;Component
public class JwtRequestFilter extends OncePerRequestFilter {Autowiredprivate JwtTokenUtil jwtTokenUtil;Autowiredprivate UserDetailsService userDetailsService;Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws ServletException, IOException {final String authorizationHeader request.getHeader(Authorization);String username null;String jwt null;if (authorizationHeader ! null authorizationHeader.startsWith(Bearer )) {jwt authorizationHeader.substring(7);username jwtTokenUtil.getUsernameFromToken(jwt);}if (username ! null SecurityContextHolder.getContext().getAuthentication() null) {UserDetails userDetails this.userDetailsService.loadUserByUsername(username);if (jwtTokenUtil.validateToken(jwt, userDetails)) {UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);}}chain.doFilter(request, response);}
}5. 登录和生成 JWT
创建一个登录接口验证用户身份并生成 JWT 返回给客户端。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;RestController
public class JwtAuthenticationController {Autowiredprivate AuthenticationManager authenticationManager;Autowiredprivate JwtTokenUtil jwtTokenUtil;Autowiredprivate UserDetailsService userDetailsService;PostMapping(value /api/login)public ResponseEntity? createAuthenticationToken(RequestBody JwtRequest authenticationRequest) throws Exception {authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());final UserDetails userDetails userDetailsService.loadUserByUsername(authenticationRequest.getUsername());final String token jwtTokenUtil.generateToken(userDetails);return ResponseEntity.ok(new JwtResponse(token));}private void authenticate(String username, String password) throws Exception {try {authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));} catch (DisabledException e) {throw new Exception(USER_DISABLED, e);} catch (BadCredentialsException e) {throw new Exception(INVALID_CREDENTIALS, e);}}
}前端Vue
在前端 Vue 应用中你需要处理 JWT 的存储和使用。
1. 登录请求
在 Vue 组件中实现用户登录请求获取 JWT 并存储到 LocalStorage。
// 登录方法
login(username, password) {return axios.post(/api/login, { username, password }).then(response {if (response.data response.data.token) {// 将 JWT Token 保存到 LocalStorage 中localStorage.setItem(jwtToken, response.data.token);}return response.data;});
}2. JWT Token 的存储和使用
在 Vue 应用中通常将 JWT Token 存储在 LocalStorage 中并在每次请求时将 Token 添加到请求的 Header 中以便后端验证用户的身份和权限。
// 设置全局的 axios 默认请求头添加 Authorization 字段
axios.defaults.headers.common[Authorization] Bearer localStorage.getItem(jwtToken);javascript
// 示例获取用户信息的方法
getUserInfo() {return axios.get(/api/userinfo).then(response {return response.data;});
}// 示例注销方法
logout() {localStorage.removeItem(jwtToken); // 清除本地存储的JWT Token// 可以选择发送注销请求到后端使后端的Token失效
}3. 路由守卫和权限控制
使用 Vue Router 的导航守卫路由守卫根据用户的登录状态和权限信息控制页面的访问。
import Vue from vue
import Router from vue-router
import Home from ./views/Home.vue
import AdminPanel from ./views/AdminPanel.vue
import Login from ./views/Login.vueVue.use(Router)const router new Router({routes: [{path: /,name: home,component: Home,meta: { requiresAuth: true } // 需要登录才能访问的页面},{path: /admin,name: admin,component: AdminPanel,meta: { requiresAuth: true, requiresAdmin: true } // 需要管理员权限才能访问的页面},{path: /login,name: login,component: Login}]
});router.beforeEach((to, from, next) {const jwtToken localStorage.getItem(jwtToken);if (to.matched.some(record record.meta.requiresAuth)) {if (!jwtToken) {next(/login); // 没有登录跳转到登录页面} else {// 验证Token是否过期等逻辑可以根据实际需求添加next();}} else {next(); // 不需要登录的页面直接放行}
});export default router;4. 处理Token过期和刷新
在使用JWT时需要处理Token过期的情况一般的做法是在前端捕获HTTP请求返回的401状态码未授权然后根据情况重新获取新的Token。
// 示例处理HTTP请求返回的401状态码
axios.interceptors.response.use(response {return response;},error {if (error.response.status 401) {// 清除本地存储的TokenlocalStorage.removeItem(jwtToken);// 跳转到登录页面或者重新获取新的Token等操作// 可以根据实际情况进行处理}return Promise.reject(error);}
);新的配置方式主要区别在于 Spring Security 中不再使用 WebSecurityConfigurerAdapter 进行配置而是通过 SecurityFilterChain 和其他配置类来实现同样的功能。下面详细介绍它们的区别和新的配置方法。 新旧版对比
旧版使用 WebSecurityConfigurerAdapter
旧版配置通常使用一个继承 WebSecurityConfigurerAdapter 的类来进行安全配置。
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests().antMatchers(/api/public/**).permitAll().anyRequest().authenticated().and().exceptionHandling().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);// 添加 JWT 过滤器http.addFilterBefore(jwtRequestFilter(), UsernamePasswordAuthenticationFilter.class);}Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}// 定义 JWT 过滤器Beanpublic JwtRequestFilter jwtRequestFilter() {return new JwtRequestFilter();}
}新版使用 SecurityFilterChain
新版配置不再使用 WebSecurityConfigurerAdapter而是通过定义 SecurityFilterChain Bean 来配置安全性。
新版配置示例
1. SecurityConfig.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;Configuration
EnableWebSecurity
EnableGlobalMethodSecurity(prePostEnabled true)
public class SecurityConfig {private final JwtRequestFilter jwtRequestFilter;private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint;private final CustomAccessDeniedHandler customAccessDeniedHandler;public SecurityConfig(JwtRequestFilter jwtRequestFilter,CustomAuthenticationEntryPoint customAuthenticationEntryPoint,CustomAccessDeniedHandler customAccessDeniedHandler) {this.jwtRequestFilter jwtRequestFilter;this.customAuthenticationEntryPoint customAuthenticationEntryPoint;this.customAccessDeniedHandler customAccessDeniedHandler;}Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests(authorizeRequests - authorizeRequests.antMatchers(/api/public/**).permitAll().anyRequest().authenticated()).exceptionHandling(exceptionHandling -exceptionHandling.authenticationEntryPoint(customAuthenticationEntryPoint).accessDeniedHandler(customAccessDeniedHandler)).sessionManagement(sessionManagement - sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)).addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);return http.build();}Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {return authenticationConfiguration.getAuthenticationManager();}
}2. 其他相关配置和组件
JwtTokenUtil处理 JWT 生成和验证的工具类。JwtRequestFilterJWT 过滤器用于在请求到达时验证 JWT。
JwtTokenUtil.java
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;Component
public class JwtTokenUtil implements Serializable {Value(${jwt.secret})private String secret;Value(${jwt.expiration})private long expiration;public String generateToken(UserDetails userDetails) {MapString, Object claims new HashMap();return Jwts.builder().setClaims(claims).setSubject(userDetails.getUsername()).setIssuedAt(new Date(System.currentTimeMillis())).setExpiration(new Date(System.currentTimeMillis() expiration * 1000)).signWith(SignatureAlgorithm.HS512, secret).compact();}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, FunctionClaims, T claimsResolver) {final Claims claims getAllClaimsFromToken(token);return claimsResolver.apply(claims);}private Claims getAllClaimsFromToken(String token) {return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();}public boolean validateToken(String token, UserDetails userDetails) {final String username getUsernameFromToken(token);return (username.equals(userDetails.getUsername()) !isTokenExpired(token));}private boolean isTokenExpired(String token) {final Date expiration getExpirationDateFromToken(token);return expiration.before(new Date());}
}JwtRequestFilter.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;Component
public class JwtRequestFilter extends OncePerRequestFilter {Autowiredprivate JwtTokenUtil jwtTokenUtil;Autowiredprivate UserDetailsService userDetailsService;Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws ServletException, IOException {final String authorizationHeader request.getHeader(Authorization);String username null;String jwt null;if (authorizationHeader ! null authorizationHeader.startsWith(Bearer )) {jwt authorizationHeader.substring(7);username jwtTokenUtil.getUsernameFromToken(jwt);}if (username ! null SecurityContextHolder.getContext().getAuthentication() null) {UserDetails userDetails this.userDetailsService.loadUserByUsername(username);if (jwtTokenUtil.validateToken(jwt, userDetails)) {UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);}}chain.doFilter(request, response);}
}新旧版区别总结 配置类的结构 旧版通过继承 WebSecurityConfigurerAdapter重写 configure(HttpSecurity http) 方法进行配置。新版通过定义 SecurityFilterChain Bean直接配置 HttpSecurity。 认证管理器 旧版直接在 WebSecurityConfigurerAdapter 中配置。新版通过 AuthenticationConfiguration 获取 AuthenticationManager。 注入过滤器 旧版使用 http.addFilterBefore(jwtRequestFilter(), UsernamePasswordAuthenticationFilter.class)。新版使用 http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)。
迁移步骤 删除 WebSecurityConfigurerAdapter 删除继承自 WebSecurityConfigurerAdapter 的类。 定义 SecurityFilterChain Bean 新建一个配置类定义 SecurityFilterChain Bean配置 HttpSecurity。 调整其他配置 根据新方式调整 AuthenticationManager 和其他相关配置。
通过以上步骤你可以从旧版的 WebSecurityConfigurerAdapter 配置迁移到新版的 SecurityFilterChain 配置同时保持应用的安全性和功能。
大致流程
引入依赖确保引入 Spring Security 和 JWT 相关的依赖。配置安全性使用 Java 配置类如 SecurityConfig来设置 HTTP 安全性、CSRF、会话管理等。实现 JWT 相关逻辑创建工具类和过滤器来处理 JWT 的生成、解析和验证。自定义异常处理编写自定义的认证入口点和访问拒绝处理器。实现用户服务实现 UserDetailsService 接口加载用户信息。编写控制器创建登录和注册接口处理用户认证和注册请求。测试与验证启动应用程序使用工具测试各个接口确保功能正常。