Spring Security 是一个强大且可扩展的框架,用于保护 Java 应用程序,尤其是基于 Spring 的应用。它提供了身份验证(验证用户身份)、授权(管理用户权限)和防护机制(如 CSRF 保护和防止会话劫持)等功能。
Spring Security 允许开发者通过灵活的配置实现安全控制,确保应用程序的数据和资源安全。通过与其他 Spring 生态系统的无缝集成,Spring Security 成为构建安全应用的理想选择。
核心概念
当我们引入 security
依赖后,访问需要授权的 url 时,会重定向到 login
页面(security 自己创建的),login
页面需要账号密码,账号默认是 user
, 密码是随机的字符串,在spring项目的输出信息中
spring-boot-starter-security
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>3.3.4</version>
</dependency>
jjwt-api
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.6</version>
</dependency>
jjwt-impl
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
jjwt-jackson
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-jackson -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
一般我们会创建一个 SecurityConfig
类,来管理我们所有与 security
相关的配置。(我们讲的是 security 5.7 版本之后的配置方法,之前的方法跟现在不太一样)
@Configuration
@EnableWebSecurity // 该注解启用 Spring Security 的 web 安全功能。
public class SecurityConfig {
}
下面的都要写到 SecurityConfig
类中
基于内存的用户认证
通过 createUser
, manager 把用户配置的账号密码添加到spring的内存中, InMemoryUserDetailsManager
类中有一个 loadUserByUsername
的方法通过账号(username)从内存中获取我们配置的账号密码,之后调用其他方法来判断前端用户输入的密码和内存中的密码是否匹配。
@Bean
public UserDetailsService userDetailsService() {
// 创建基于内存的用户信息管理器
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(
// 创建UserDetails对象,用于管理用户名、用户密码、用户角色、用户权限等内容
User.withDefaultPasswordEncoder().username("user").password("user123").roles("USER").build()
);
// 如果自己配置的有账号密码, 那么上面讲的 user 和 随机字符串 的默认密码就不能用了
return manager;
}
当我们点进 InMemoryUserDetailsManager
中 可以发现它实现了 UserDetailsManager
和 UserDetailsPasswordService
接口,其中 UserDetailsManager
接口继承的 UserDetailsService
接口中就有 loadUserByUsername
方法
基于数据库的用户认证
上面讲到,spring security 是通过 loadUserByUsername
方法来获取 User
并用这个 User
来判断用户输入的密码是否正确。所以我们只需要继承 UserDetailsService
接口并重写 loadUserByUsername
方法即可
下面的样例我用的 mybatis-plus 来查询数据库中的 user
, 然后通过当前查询到的 user
返回特定的 UserDetails
对象
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
queryWrapper.eq("username", username); // 这里不止可以用username,你可以自定义,主要根据你自己写的查询逻辑
User user = userMapper.selectOne(queryWrapper);
if (user == null) {
throw new UsernameNotFoundException(username);
}
return new UserDetailsImpl(user); // UserDetailsImpl 是我们实现的类
}
}
UserDetailsImpl
是实现了 UserDetails
接口的类。UserDetails
接口是 Spring Security 身份验证机制的基础,通过实现该接口,开发者可以定义自己的用户模型,并提供用户相关的信息,以便进行身份验证和权限检查。
@Data
@AllArgsConstructor
@NoArgsConstructor // 这三个注解可以帮我们自动生成 get、set、有参、无参构造函数
public class UserDetailsImpl implements UserDetails {
private User user; // 通过有参构造函数填充赋值的
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of();
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() { // 检查账户是否 没过期。
return true;
}
@Override
public boolean isAccountNonLocked() { // 检查账户是否 没有被锁定。
return true;
}
@Override
public boolean isCredentialsNonExpired() { //检查凭据(密码)是否 没过期。
return true;
}
@Override
public boolean isEnabled() { // 检查账户是否启用。
return true;
}
// 这个方法是 @Data注解 会自动帮我们生成,用来获取 loadUserByUsername 中最后我们返回的创建UserDetailsImpl对象时传入的User。
// 如果你的字段包含 username和password 的话可以用强制类型转换, 把 UserDetailsImpl 转换成 User。如果不能强制类型转换的话就需要用到这个方法了
public User getUser() {
return user;
}
}
下面这个是 security
的默认配置。我们可以修改并把它加到spring容器中,完成我们特定的需求。
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// 开启授权保护
.authorizeHttpRequests(authorize -> authorize
// 不需要认证的地址有哪些
.requestMatchers("/blog/**", "/public/**", "/about").permitAll() // ** 通配符
// 对所有请求开启授权保护
.anyRequest().
// 已认证的请求会被自动授权
authenticated()
)
// 使用默认的登陆登出页面进行授权登陆
.formLogin(Customizer.withDefaults())
// 启用“记住我”功能的。允许用户在关闭浏览器后,仍然保持登录状态,直到他们主动注销或超出设定的过期时间。
.rememberMe(Customizer.withDefaults());
// 关闭 csrf CSRF(跨站请求伪造)是一种网络攻击,攻击者通过欺骗已登录用户,诱使他们在不知情的情况下向受信任的网站发送请求。
http.csrf(csrf -> csrf.disable());
return http.build();
}
关于上面放行路径写法的一些细节问题:
如果在 application.properities
中配置的有 server.servlet.context-path=/api
前缀的话,在放行路径中不需要写 /api
。
如果 @RequestMapping(value = "/test/")
中写的是 /test/
, 那么放行路径必须也写成 /test/
, (/test
)是不行的,反之亦然。
如果 @RequestMapping(value = "/test")
链接 /test
后面要加查询字符的话(/test?type=0
),不要写成 @RequestMapping(value = "/test/")
上面都是一些细节问题,但是遇到 bug 的时候不容易发现。(笔者初学时找了一个小时,… … .)
下面这个是我常用的配置,配置了jwt,加密的类,过滤器启用 jwt,而不是session
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(CsrfConfigurer::disable) // 基于token,不需要csrf
.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 基于token,不需要session
.authorizeHttpRequests((authz) -> authz
.requestMatchers("/login/", "/getPicCheckCode").permitAll() // 放行api
.requestMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
Spring Security 提供了多种加密和安全机制来保护用户的敏感信息,尤其是在用户身份验证和密码管理方面。(我们只讲默认的 BCryptPasswordEncoder
)
在应用程序中,用户密码是最敏感的数据之一。为了防止密码泄露,即使数据库被攻击者获取,密码也应以加密形式存储。加密可以保护用户的隐私,并在一定程度上增加安全性。
Spring Security 提供了 PasswordEncoder
接口,用于定义密码的加密和验证方法。主要有以下几种实现:
BCrypt 是一种安全的密码哈希算法,具有以下特点:
可以在 Spring Security 的配置类中定义 PasswordEncoder
bean,例如:
@Configuration
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
// 也可用有参构造,取值范围是 4 到 31,默认值为 10。数值越大,加密计算越复杂
return new BCryptPasswordEncoder();
}
}
在注册用户或更新密码时,可以使用 PasswordEncoder
来加密密码:
@Autowired
private PasswordEncoder passwordEncoder;
public void registerUser(String username, String rawPassword) {
String encryptedPassword = passwordEncoder.encode(rawPassword);
// 保存用户信息到数据库,包括加密后的密码
}
在用户登录时,可以使用 matches
方法验证输入的密码与存储的加密密码是否匹配:
public boolean login(String username, String rawPassword) {
// 从数据库中获取用户信息,包括加密后的密码
String storedEncryptedPassword = // 从数据库获取;
return passwordEncoder.matches(rawPassword, storedEncryptedPassword);
}
前端:
POST /api/login
)。后端:
AuthenticationManager
进行身份验证。localStorage
或 sessionStorage
中,以便后续请求使用。fetch('/api/protected-endpoint', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`
}
});
OncePerRequestFilter
以拦截请求,提取 JWT,并验证其有效性。由于 JWT 是无状态的,后端不需要记录会话状态。用户可以通过前端操作(例如,删除存储的 JWT)来“退出登录”。可以实现一个注销接口,用于前端执行相关逻辑。
作用:OncePerRequestFilter
是 Spring Security 提供的一个抽象类,确保在每个请求中只执行一次特定的过滤逻辑。它是实现自定义过滤器的基础,通常用于对请求进行预处理或后处理。(实现 JWT
会用到这个接口)
功能:提供了一种机制,以确保过滤器的逻辑在每个请求中只执行一次,非常适合需要对每个请求进行处理的场景。通过继承该类,可以轻松实现自定义过滤器适合用于记录日志、身份验证、权限检查等场景。
实现:继承 OncePerRequestFilter
类,并重写 doFilterInternal
方法。
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;
public class CustomFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// 自定义过滤逻辑,例如记录请求日志
System.out.println("Request URI: " + request.getRequestURI());
// 继续执行过滤链
filterChain.doFilter(request, response);
}
}
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.addFilterBefore(new CustomFilter(), UsernamePasswordAuthenticationFilter.class);
// 其他配置...
return http.build();
}
}
基于我们上面引入的三个 JWT
相关的依赖,写JwtUtil
类
JwtUtil
类提供了生成和解析 JWT 的功能,是实现基于 JWT 的认证和授权的重要工具。@Component
public class JwtUtil {
public static final long JWT_TTL = 60 * 60 * 1000L * 24 * 14; // 有效期14天
public static final String JWT_KEY = "SDFGjhdsfalshdfHFdsjkdsfds121232131afasdfac";
public static String getUUID() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
public static String createJWT(String subject) {
JwtBuilder builder = getJwtBuilder(subject, null, getUUID());
return builder.compact();
}
private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
SecretKey secretKey = generalKey();
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
if (ttlMillis == null) {
ttlMillis = JwtUtil.JWT_TTL;
}
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
return Jwts.builder()
.id(uuid)
.subject(subject)
.issuer("sg")
.issuedAt(now)
.signWith(secretKey)
.expiration(expDate);
}
public static SecretKey generalKey() {
byte[] encodeKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
return new SecretKeySpec(encodeKey, 0, encodeKey.length, "HmacSHA256");
}
public static Claims parseJWT(String jwt) throws Exception {
SecretKey secretKey = generalKey();
return Jwts.parser()
.verifyWith(secretKey)
.build()
.parseSignedClaims(jwt)
.getPayload();
}
}
@Component
:将这个类标记为 Spring 组件,允许 Spring 管理该类的生命周期,便于依赖注入。JWT_TTL
:JWT 的有效期,设置为 14 天(单位为毫秒)。JWT_KEY
:用于签名的密钥字符串,经过 Base64 编码。它是生成和验证 JWT 的关键。public static String getUUID() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
jti
),确保每个 JWT 都是唯一的。public static String createJWT(String subject) {
JwtBuilder builder = getJwtBuilder(subject, null, getUUID());
return builder.compact();
}
subject
是 JWT 的主题,通常是用户的身份信息。getJwtBuilder
方法生成一个 JWT 构建器,并将其压缩为字符串返回。private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
//...
}
setId(uuid)
:设置 JWT 的唯一标识。setSubject(subject)
:设置 JWT 的主题。setIssuer("sg")
:设置 JWT 的发行者,通常是你的应用或服务名称。setIssuedAt(now)
:设置 JWT 的签发时间。signWith(signatureAlgorithm, secretKey)
:使用指定的算法和密钥进行签名。setExpiration(expDate)
:设置 JWT 的过期时间。public static SecretKey generalKey() {
byte[] encodeKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
return new SecretKeySpec(encodeKey, 0, encodeKey.length, "HmacSHA256");
}
JWT_KEY
进行 Base64 解码,生成一个 SecretKey
对象,用于 JWT 的签名和验证。使用 HMAC SHA-256 算法进行签名。public static Claims parseJWT(String jwt) throws Exception {
SecretKey secretKey = generalKey();
return Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(jwt)
.getBody();
}
jwt
是要解析的 JWT 字符串。Claims
对象),其中包含了 JWT 的主题、发行者、过期时间等信息。先加一个依赖 JetBrains Java Annotations
, 下面的代码会用到其中的一个注解 @NotNull
。
这个 JwtAuthenticationTokenFilter
类是一个实现了 OncePerRequestFilter
接口自定义的 Spring Security 过滤器。
JwtAuthenticationTokenFilter
通过 JWT 对用户进行身份验证,确保请求中携带的 JWT 是有效的,并根据 JWT 提供的用户信息设置认证状态。import io.jsonwebtoken.Claims;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private UserMapper userMapper;
@Override
protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader("Authorization");
if (!StringUtils.hasText(token) || !token.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
token = token.substring(7);
String userid;
try {
Claims claims = JwtUtil.parseJWT(token);
userid = claims.getSubject();
} catch (Exception e) {
throw new RuntimeException(e);
}
User user = userMapper.selectById(Integer.parseInt(userid));
if (user == null) {
throw new RuntimeException("用户名未登录");
}
UserDetailsImpl loginUser = new UserDetailsImpl(user);
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginUser, null, null);
// 如果是有效的jwt,那么设置该用户为认证后的用户
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(request, response);
}
}
@Component
:标记该类为 Spring 组件,使其能够被 Spring 扫描并注册到应用上下文中。OncePerRequestFilter
:确保每个请求只调用一次该过滤器,适合进行请求预处理。@Autowired
private UserMapper userMapper;
UserMapper
:一个数据访问对象(DAO),用于与数据库交互,以便根据用户 ID 查询用户信息。通过 Spring 的依赖注入机制自动注入。String token = request.getHeader("Authorization");
if (!StringUtils.hasText(token) || !token.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
Authorization
字段的值,检查是否包含 JWT。filterChain.doFilter(request, response)
继续执行下一个过滤器,返回。token = token.substring(7); // 去掉 "Bearer " 前缀
String userid;
try {
Claims claims = JwtUtil.parseJWT(token);
userid = claims.getSubject();
} catch (Exception e) {
throw new RuntimeException(e);
}
JwtUtil.parseJWT(token)
解析 JWT,提取出用户 ID (userid
)。如果解析失败,会抛出异常。User user = userMapper.selectById(Integer.parseInt(userid));
if (user == null) {
throw new RuntimeException("用户名未登录");
}
UserDetailsImpl loginUser = new UserDetailsImpl(user);
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginUser, null, null);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
UserDetailsImpl
对象,将查询到的用户信息封装。UsernamePasswordAuthenticationToken
对象,表示用户的认证信息,并将其设置到 Spring Security 的 SecurityContextHolder
中,以便后续请求能够访问到用户的认证信息。filterChain.doFilter(request, response);
filterChain.doFilter(request, response)
,继续执行后续的过滤器和最终的请求处理。JwtAuthenticationTokenFilter
过滤器把我们的过滤器应用到 spring security
中
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
将 AuthenticationManager
对象添加到spring容器中.(添加到我们创建的 SecurityConfig
配置类中)
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
@Autowired
private AuthenticationManager authenticationManager;
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
// 从数据库中对比查找,如果找到了会返回一个带有认证的封装后的用户,否则会报错,自动处理。(这里我们假设我们配置的security是基于数据库查找的)
Authentication authenticate = authenticationManager.authenticate(authenticationToken);
// 获取认证后的用户
User user = (User ) authenticate.getPrincipal(); // 如果强制类型转换报错的话,可以用我们实现的 `UserDetailsImpl` 类中的 getUser 方法了
String jwt = JwtUtil.createJWT(user.getId().toString());
以下是获取当前用户认证信息的具体步骤:
// SecurityContextHolder 是一个存储安全上下文的工具类,提供了一个全局访问点,用于获取当前请求的安全上下文。
// SecurityContext 是当前线程的安全上下文,包含了当前用户的认证信息(即 Authentication 对象)。
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
User user = (User ) authenticate.getPrincipal();
// 另一种获取 User 的方法
UsernamePasswordAuthenticationToken authenticationToken = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
UserDetailsImpl loginUser = (UserDetailsImpl) authenticationToken.getPrincipal();
User user = userDetails.getUser();
authenticationManager
和 SecurityContext
获取 Authentication
Authentication
接口包含以下重要方法:
getPrincipal()
:返回当前用户的身份,通常是用户的详细信息(如用户名)。getCredentials()
:返回用户的凭证(如密码),但通常不直接使用此方法。getAuthorities()
:返回用户的权限列表(角色或权限)。Spring Security 的授权机制用于控制用户对应用程序资源的访问权限。授权通常是基于用户角色或权限的,以下是对 Spring Security 授权的详细讲解。
GrantedAuthority
接口表示用户的权限。每个用户的权限可以通过实现该接口的对象来表示。Spring Security 支持多种授权方式,主要包括:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN") // 只有 ADMIN 角色可以访问
.antMatchers("/user/**").hasAnyRole("USER", "ADMIN") // USER 和 ADMIN 角色可以访问
.anyRequest().authenticated(); // 其他请求需要认证
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/edit/**").hasAuthority("EDIT_PRIVILEGE") // 仅具有 EDIT_PRIVILEGE 权限的用户可以访问
.anyRequest().authenticated();
}
如果需要更复杂的授权逻辑,可以实现自定义的 AccessDecisionVoter
或 AccessDecisionManager
。
AccessDecisionVoter
的调用,确定用户是否有权访问请求的资源。以下接口和类用于处理不同的安全事件,提供了自定义处理的能力:
AuthenticationSuccessHandler
接口,并重写 onAuthenticationSuccess
方法。public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
}
}
// 让我们实现的类生效
http.formLogin(form ->
form.successHandler(new MyAuthenticationSuccessHandler()));
AuthenticationFailureHandler
接口,并重写 onAuthenticationFailure
方法。public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
}
}
// 让我们实现的类生效
http.formLogin(form ->
form.failureHandler(new MyAuthenticationFailureHandler()));
LogoutSuccessHandler
接口,并重写 onLogoutSuccess
方法。public class MyLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
}
}
// 让我们实现的类生效
http.logout(logout -> {
logout.logoutSuccessHandler(new MyLogoutSuccessHandler());
});
AuthenticationEntryPoint
接口,并重写 commence
方法。// 重写 AuthenticationEntryPoint 接口
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
}
}
// 让我们重写的类生效
http.exceptionHandling(exception -> {
exception.authenticationEntryPoint(new MyAuthenticationEntryPoint());
});
SessionInformationExpiredStrategy
接口,并重写 onExpiredSession
方法。// 重写 SessionInformationExpiredStrategy 接口
public class MySessionInformationExpiredStrategy implements SessionInformationExpiredStrategy {
@Override
public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
}
}
// 让我们重写的类生效
http.sessionManagement(session -> {
session.maximumSessions(1).expiredSessionStrategy(new MySessionInformationExpiredStrategy());
});
作用:用于处理用户访问被拒绝的情况。当用户尝试访问没有权限的资源时,Spring Security 会调用实现了 AccessDeniedHandler
接口的处理器。
功能:可以自定义拒绝访问后的响应行为,比如重定向到特定的错误页面、返回错误信息或 JSON 响应。
实现:实现 AccessDeniedHandler
接口,并重写 handle
方法。
// 重写 AccessDeniedHandler 接口
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
}
}
// 在安全配置中注册自定义的 AccessDeniedHandler
http.exceptionHandling(exception -> {
exception.accessDeniedHandler(new MyAccessDeniedHandler());
});
});
文章到这里就结束了~