
Spring Security 是一个强大且可扩展的框架用于保护 Java 应用程序尤其是基于 Spring 的应用。它提供了身份验证验证用户身份、授权管理用户权限和防护机制如 CSRF 保护和防止会话劫持等功能。Spring Security 允许开发者通过灵活的配置实现安全控制确保应用程序的数据和资源安全。通过与其他 Spring 生态系统的无缝集成Spring Security 成为构建安全应用的理想选择。核心概念身份验证 (Authentication): 验证用户的身份例如用户名/密码。授权 (Authorization): 确定用户是否有权限访问特定资源。安全上下文 (Security Context): 存储已认证用户的详细信息应用程序中可以访问。1、准备工作1.1 引入依赖当我们引入security依赖后访问需要授权的 url 时会重定向到login页面security 自己创建的login页面需要账号密码账号默认是user, 密码是随机的字符串在spring项目的输出信息中spring-boot-starter-security!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId version3.3.4/version /dependencyjjwt-api!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -- dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.12.6/version /dependencyjjwt-impl!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -- dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-impl/artifactId version0.12.6/version scoperuntime/scope /dependencyjjwt-jackson!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-jackson -- dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-jackson/artifactId version0.12.6/version scoperuntime/scope /dependency一般我们会创建一个SecurityConfig类来管理我们所有与security相关的配置。我们讲的是 security 5.7 版本之后的配置方法之前的方法跟现在不太一样Configuration EnableWebSecurity // 该注解启用 Spring Security 的 web 安全功能。 public class SecurityConfig { }下面的都要写到SecurityConfig类中1.2 用户认证的配置基于内存的用户认证通过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 { QueryWrapperUser queryWrapper new QueryWrapperUser(); 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; } }1.3 基本的配置下面这个是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?type0不要写成RequestMapping(value /test/)上面都是一些细节问题但是遇到 bug 的时候不容易发现。笔者初学时找了一个小时.. .. .下面这个是我常用的配置配置了jwt加密的类过滤器启用 jwt而不是sessionConfiguration 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(); } }2、加密Spring Security 提供了多种加密和安全机制来保护用户的敏感信息尤其是在用户身份验证和密码管理方面。我们只讲默认的BCryptPasswordEncoder1. 密码加密的重要性在应用程序中用户密码是最敏感的数据之一。为了防止密码泄露即使数据库被攻击者获取密码也应以加密形式存储。加密可以保护用户的隐私并在一定程度上增加安全性。2. Spring Security 的加密机制2.1 PasswordEncoder 接口Spring Security 提供了PasswordEncoder接口用于定义密码的加密和验证方法。主要有以下几种实现BCryptPasswordEncoder基于 BCrypt 算法具有适应性和强大的加密强度。它可以根据需求自动调整加密的复杂性。NoOpPasswordEncoder不执行加密适用于开发和测试环境不建议在生产环境中使用。Pbkdf2PasswordEncoder、Argon2PasswordEncoder等这些都是基于不同算法的实现具有不同的安全特性。2.2 BCrypt 算法BCrypt 是一种安全的密码哈希算法具有以下特点盐值Salt每次加密时都会生成一个随机盐值确保相同的密码在每次加密时生成不同的哈希值。适应性通过增加计算复杂性如工作因子可以提高密码的加密强度。3. 使用 PasswordEncoder3.1 配置 PasswordEncoder可以在 Spring Security 的配置类中定义PasswordEncoderbean例如Configuration public class SecurityConfig { Bean public PasswordEncoder passwordEncoder() { // 也可用有参构造取值范围是 4 到 31默认值为 10。数值越大加密计算越复杂 return new BCryptPasswordEncoder(); } }3.2 加密密码在注册用户或更新密码时可以使用PasswordEncoder来加密密码Autowired private PasswordEncoder passwordEncoder; public void registerUser(String username, String rawPassword) { String encryptedPassword passwordEncoder.encode(rawPassword); // 保存用户信息到数据库包括加密后的密码 }3.3 验证密码在用户登录时可以使用matches方法验证输入的密码与存储的加密密码是否匹配public boolean login(String username, String rawPassword) { // 从数据库中获取用户信息包括加密后的密码 String storedEncryptedPassword // 从数据库获取; return passwordEncoder.matches(rawPassword, storedEncryptedPassword); }3、前后端分离1. 用户认证流程1.1 用户登录前端用户在登录界面输入用户名和密码。前端将这些凭证以 JSON 格式发送到后端的登录 API例如POST /api/login。后端Spring Security 接收请求使用AuthenticationManager进行身份验证。如果认证成功后端生成一个 JWTJSON Web Token或其他认证令牌并将其返回给前端。2. 使用 JWT 进行用户认证2.1 前端存储 JWT前端收到 JWT 后可以将其存储在localStorage或sessionStorage中以便后续请求使用。2.2 发送受保护请求在发送需要认证的请求时前端将 JWT 添加到请求头中fetch(/api/protected-endpoint, { method: GET, headers: { Authorization: Bearer ${token} } });2.3 后端解析 JWTSpring Security 通过过滤器来解析和验证 JWT。可以自定义一个OncePerRequestFilter以拦截请求提取 JWT并验证其有效性。3. 退出登录由于 JWT 是无状态的后端不需要记录会话状态。用户可以通过前端操作例如删除存储的 JWT来“退出登录”。可以实现一个注销接口用于前端执行相关逻辑。4. 保护敏感信息确保 HTTPS在前后端通信中使用 HTTPS确保传输中的数据安全。令牌过期设置 JWT 的有效期过期后需要用户重新登录。刷新令牌可以实现刷新令牌的机制以提高用户体验。4. 实现 jwt4.1 OncePerRequestFilter作用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); } }注册过滤器可以在 Spring Security 配置类中注册自定义的过滤器。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(); } }4.2 生成 JWT基于我们上面引入的三个JWT相关的依赖写JwtUtil类功能JwtUtil类提供了生成和解析 JWT 的功能是实现基于 JWT 的认证和授权的重要工具。应用场景可以在用户登录后生成 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(); } }1. 类注解与常量Component将这个类标记为 Spring 组件允许 Spring 管理该类的生命周期便于依赖注入。JWT_TTLJWT 的有效期设置为 14 天单位为毫秒。JWT_KEY用于签名的密钥字符串经过 Base64 编码。它是生成和验证 JWT 的关键。2. UUID 生成public static String getUUID() { return UUID.randomUUID().toString().replaceAll(-, ); }作用生成一个唯一的 UUID去掉了其中的连字符。这通常用作 JWT 的 IDjti确保每个 JWT 都是唯一的。3. 创建 JWTpublic static String createJWT(String subject) { JwtBuilder builder getJwtBuilder(subject, null, getUUID()); return builder.compact(); }参数subject是 JWT 的主题通常是用户的身份信息。功能调用getJwtBuilder方法生成一个 JWT 构建器并将其压缩为字符串返回。4. JWT 构建器private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) { //... }功能生成一个 JWT 的构建器配置 JWT 的各个属性包括setId(uuid)设置 JWT 的唯一标识。setSubject(subject)设置 JWT 的主题。setIssuer(sg)设置 JWT 的发行者通常是你的应用或服务名称。setIssuedAt(now)设置 JWT 的签发时间。signWith(signatureAlgorithm, secretKey)使用指定的算法和密钥进行签名。setExpiration(expDate)设置 JWT 的过期时间。5. 生成密钥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 算法进行签名。6. 解析 JWTpublic static Claims parseJWT(String jwt) throws Exception { SecretKey secretKey generalKey(); return Jwts.parserBuilder() .setSigningKey(secretKey) .build() .parseClaimsJws(jwt) .getBody(); }参数jwt是要解析的 JWT 字符串。功能使用之前生成的密钥解析 JWT。如果 JWT 有效将返回 JWT 的声明Claims对象其中包含了 JWT 的主题、发行者、过期时间等信息。该方法可能会抛出异常表示 JWT 无效或解析失败。4.3 应用 JWT先加一个依赖JetBrains Java Annotations, 下面的代码会用到其中的一个注解NotNull。这个JwtAuthenticationTokenFilter类是一个实现了OncePerRequestFilter接口自定义的 Spring Security 过滤器。功能JwtAuthenticationTokenFilter通过 JWT 对用户进行身份验证确保请求中携带的 JWT 是有效的并根据 JWT 提供的用户信息设置认证状态。应用场景通常用于保护需要身份验证的 API 接口确保只有经过认证的用户才能访问资源。该过滤器通常放置在 Spring Security 过滤链的合适位置以确保在处理请求之前进行身份验证。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); } }1. 类注解与继承Component标记该类为 Spring 组件使其能够被 Spring 扫描并注册到应用上下文中。继承OncePerRequestFilter确保每个请求只调用一次该过滤器适合进行请求预处理。2. 依赖注入Autowired private UserMapper userMapper;UserMapper一个数据访问对象DAO用于与数据库交互以便根据用户 ID 查询用户信息。通过 Spring 的依赖注入机制自动注入。3 获取 JWTString token request.getHeader(Authorization); if (!StringUtils.hasText(token) || !token.startsWith(Bearer )) { filterChain.doFilter(request, response); return; }从请求头中获取Authorization字段的值检查是否包含 JWT。如果没有 JWT 或格式不正确直接调用filterChain.doFilter(request, response)继续执行下一个过滤器返回。4 解析 JWTtoken token.substring(7); // 去掉 Bearer 前缀 String userid; try { Claims claims JwtUtil.parseJWT(token); userid claims.getSubject(); } catch (Exception e) { throw new RuntimeException(e); }从 token 中去掉 Bearer 前缀只保留 JWT 部分。调用JwtUtil.parseJWT(token)解析 JWT提取出用户 ID (userid)。如果解析失败会抛出异常。5 查询用户信息User user userMapper.selectById(Integer.parseInt(userid)); if (user null) { throw new RuntimeException(用户名未登录); }根据解析出的用户 ID 从数据库中查询用户信息。如果用户不存在抛出异常。6 设置安全上下文UserDetailsImpl loginUser new UserDetailsImpl(user); UsernamePasswordAuthenticationToken authenticationToken new UsernamePasswordAuthenticationToken(loginUser, null, null); SecurityContextHolder.getContext().setAuthentication(authenticationToken);创建一个自定义的UserDetailsImpl对象将查询到的用户信息封装。创建一个UsernamePasswordAuthenticationToken对象表示用户的认证信息并将其设置到 Spring Security 的SecurityContextHolder中以便后续请求能够访问到用户的认证信息。4. 继续过滤链filterChain.doFilter(request, response);调用filterChain.doFilter(request, response)继续执行后续的过滤器和最终的请求处理。4.4 注册自定义的JwtAuthenticationTokenFilter过滤器把我们的过滤器应用到spring security中http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);5、常用操作5.1 配置 AuthenticationManager将AuthenticationManager对象添加到spring容器中.添加到我们创建的SecurityConfig配置类中Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { return authConfig.getAuthenticationManager(); }5.2 验证当前用户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());5.3 获取当前用户的认证信息以下是获取当前用户认证信息的具体步骤// 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();5.4 对比authenticationManager和SecurityContext获取AuthenticationSecurityContextHolder.getContext().getAuthentication():这个方法获取当前线程的安全上下文中的Authentication对象。通常在用户已经被认证后使用用于获取当前用户的信息如身份、权限等。authenticationManager.authenticate(authenticationToken):这个方法是用于实际执行身份验证的过程。它接收一个凭证如用户名和密码并返回一个经过验证的Authentication对象或者抛出异常。在用户登录或验证时使用属于身份验证的实际逻辑。5.5 Authentication 接口Authentication接口包含以下重要方法getPrincipal()返回当前用户的身份通常是用户的详细信息如用户名。getCredentials()返回用户的凭证如密码但通常不直接使用此方法。getAuthorities()返回用户的权限列表角色或权限。6、授权Spring Security 的授权机制用于控制用户对应用程序资源的访问权限。授权通常是基于用户角色或权限的以下是对 Spring Security 授权的详细讲解。1. 授权的基本概念授权Authorization指的是决定用户是否有权限访问特定资源的过程。在用户认证成功后系统会根据预定义的规则来判断用户是否可以访问某些资源。2. 授权的主要组成部分2.1 权限与角色权限Permission通常指对特定资源的操作能力如“读取”、“写入”或“删除”。角色Role一组权限的集合。例如管理员角色可能具有所有权限而普通用户角色可能只有读取权限。2.2 GrantedAuthority 接口Spring Security 使用GrantedAuthority接口表示用户的权限。每个用户的权限可以通过实现该接口的对象来表示。3. 授权方式Spring Security 支持多种授权方式主要包括3.1 基于角色的授权定义角色通常在用户注册或用户管理中定义。配置角色授权可以在 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(); // 其他请求需要认证 }3.2 基于权限的授权定义权限与角色类似定义更细粒度的权限。配置权限授权在配置类中设置基于权限的访问控制。Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers(/edit/**).hasAuthority(EDIT_PRIVILEGE) // 仅具有 EDIT_PRIVILEGE 权限的用户可以访问 .anyRequest().authenticated(); }4. 自定义授权逻辑如果需要更复杂的授权逻辑可以实现自定义的AccessDecisionVoter或AccessDecisionManager。4.1 AccessDecisionVoter负责评估用户的访问请求返回授权决策。4.2 AccessDecisionManager管理多个AccessDecisionVoter的调用确定用户是否有权访问请求的资源。7、其他自定义行为以下接口和类用于处理不同的安全事件提供了自定义处理的能力1. AuthenticationSuccessHandler作用用于处理用户成功认证后的逻辑。功能可以自定义成功登录后的跳转行为比如重定向到特定页面、返回 JSON 响应等。实现实现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()));2. AuthenticationFailureHandler作用用于处理用户认证失败的逻辑。功能可以自定义失败登录后的行为比如返回错误信息、重定向到登录页面并显示错误提示等。实现实现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()));3. LogoutSuccessHandler作用用于处理用户成功登出的逻辑。功能可以自定义注销成功后的行为比如重定向到登录页面、显示注销成功的消息等。实现实现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()); });4. AuthenticationEntryPoint作用用于处理未认证用户访问受保护资源时的逻辑。功能可以自定义未认证用户的响应比如返回 401 状态码、重定向到登录页面等。实现实现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()); });5. SessionInformationExpiredStrategy作用用于处理用户会话过期的逻辑。功能可以自定义会话超时后的响应比如重定向到登录页面或返回 JSON 响应等。实现实现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()); });6. AccessDeniedHandler作用用于处理用户访问被拒绝的情况。当用户尝试访问没有权限的资源时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()); }); });