构建安全稳定的应用:Spring Security 实用指南

九天猎人  金牌会员 | 2024-8-2 07:15:28 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 656|帖子 656|积分 1968

媒介

在现代 Web 应用步伐中,安全性是至关重要的一个方面。Spring Security 作为一个功能强大且广泛使用的安全框架,为 Java 应用步伐提供了全面的安全办理方案。本文将深入介绍 Spring Security 的基本概念、焦点功能以及如安在应用步伐中使用它来实现认证和授权。
一、Spring Security

Spring Security 是一个基于 Spring 的安全性框架,用于提供身份验证、授权、攻击防护等安全服务。它构建在 Spring 框架之上,利用依靠注入和 AOP 等功能,使得集成到现有的 Spring 应用步伐中非常简单。
在开始深入相识 Spring Security 之前,我们必要相识几个焦点概念:

  • Authentication(认证):验证用户的身份,通常是通过用户名和暗码举行。
  • Authorization(授权):确定用户是否有权限执行特定操作或访问特定资源。
  • Principal(主体):代表当前用户的抽象概念,通常是一个实现了 UserDetails 接口的对象。
  • Granted Authority(授权权限):表示用户具有的权限,通常是角色或权限的集合。
  • Access Control(访问控制):定义了哪些用户可以访问应用步伐的哪些部门以及怎样限制对资源的访问。
二、快速入门


  • 添加 Spring Security 依靠项:首先,在 Spring Boot 项目中添加 Spring Security 的依靠项。
    1. <dependencies>
    2.   <!-- Spring Boot Web Starter 依赖,包含了开发 web 应用所需的所有基础依赖 -->
    3.   <dependency>
    4.     <groupId>org.springframework.boot</groupId>
    5.     <artifactId>spring-boot-starter-web</artifactId>
    6.   </dependency>
    7.   <!-- Spring Boot Security Starter 依赖,包含了开发安全应用所需的所有基础依赖 -->
    8.   <dependency>
    9.     <groupId>org.springframework.boot</groupId>
    10.     <artifactId>spring-boot-starter-security</artifactId>
    11.   </dependency>
    12. </dependencies>
    13. <dependencyManagement>
    14.   <dependencies>
    15.     <!-- Spring Boot Dependencies POM,用于管理 Spring Boot 项目的所有依赖的版本 -->
    16.     <dependency>
    17.       <groupId>org.springframework.boot</groupId>
    18.       <artifactId>spring-boot-dependencies</artifactId>
    19.       <version>2.7.5</version>
    20.       <type>pom</type>
    21.       <scope>import</scope>
    22.     </dependency>
    23.   </dependencies>
    24. </dependencyManagement>
    复制代码
  • 配置安全战略:创建一个配置类来配置 Spring Security 的行为。
    1. @Configuration
    2. @EnableWebSecurity // 注解启用 Spring Security 的 web 安全支持
    3. public class SecurityConfig {
    4. }
    复制代码
  • 定义一个访问端点:定义一个测试使用的访问端点
    1. @RestController
    2. @RequestMapping("/test")
    3. public class TestController {
    4.     @RequestMapping("/hello")
    5.     public String hello() {
    6.         return "hello spring security";
    7.     }
    8. }
    复制代码
  • 运行应用步伐:运行 Spring Boot 应用步伐,并尝试访问端点 localhost:8080/test/hello。

    默认账号是 user,暗码如下图所示:

    输入默认账号、暗码之后:

三、焦点功能

3.1 身份验证(Authentication)

Spring Security 提供多种身份验证机制,包罗基本认证、表单认证、OAuth、LDAP 等。支持自定义身份验证流程,可以根据应用步伐的需求举行定制。开发人员可以根据必要配置请求路径是否必要认证才能访问。例如:
  1. @Configuration
  2. @EnableWebSecurity
  3. public class SecurityConfig {
  4.     @Bean
  5.     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  6.         http.authorizeRequests()
  7.         .requestMatchers(new AntPathRequestMatcher("/test/hello")).permitAll() // 允许所有用户访问 "/test/hello" 路径
  8.         .anyRequest().authenticated(); // 表示所有其他的请求都需要经过认证
  9.         return http.build();
  10.     }
  11. }
复制代码
3.2 授权(Authorization)

Spring Security 可以基于角色(Role-Based Access Control)和权限(Permission-Based Access Control)的访问控制。
  1. @Configuration
  2. @EnableWebSecurity
  3. public class SecurityConfig {
  4.     @Bean
  5.     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  6.         http.authorizeRequests()
  7.         // 只有具有 "add" 权限的用户才能访问 "/test/hello" 路径
  8.         .requestMatchers(new AntPathRequestMatcher("/test/hello")).hasAuthority("add");
  9.         return http.build();
  10.     }
  11. }
复制代码
3.3 漏洞防护(Protection Against Exploits)

Spring Security 可以防范常见的攻击,如跨站点请求伪造(CSRF)、点击挟制等。例如,通过如下配置可开启 CSRF 掩护:
  1. @Configuration
  2. @EnableWebSecurity
  3. public class SecurityConfig {
  4.     @Bean
  5.     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  6.         // 配置跨站请求伪造(CSRF)的保护
  7.         http.csrf();
  8.         return http.build();
  9.     }
  10. }
复制代码
四、高级功能

4.1 会话管理(Session Management)

Spring Security 提供了会话管理的功能,包罗会话超时、并发登录限制、会话固定攻击防护等功能。例如,可以通过如下配置开启并发登录限制:
  1. @Configuration
  2. @EnableWebSecurity
  3. public class SecurityConfig {
  4.     @Bean
  5.     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  6.         http.formLogin() // 启用了表单登录
  7.         .and()
  8.         .authorizeRequests().anyRequest().authenticated() // 所有请求都需要经过认证
  9.         .and()
  10.         .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 设置会话创建策略为无状态,即 Spring Security 不会创建会话
  11.         .sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true); // 设置每个用户的最大并发会话数为 1,并且当达到最大并发会话数时,阻止新的登录请求
  12.         return http.build();
  13.     }
  14. }
复制代码
4.2 暗码编码(Password Encoding)

Spring Security 提供暗码加密和验证机制,确保用户暗码的安全性。例如,要使用 BCrypt 加密,只需举行如下配置即可:
  1. @Configuration
  2. @EnableWebSecurity
  3. public class SecurityConfig {
  4.     @Bean
  5.     public PasswordEncoder passwordEncoder() {
  6.         return new BCryptPasswordEncoder();
  7.     }
  8. }
复制代码
4.3 基于注解的方法级安全控制

Spring Security 允许在方法上使用注解的方式举行访问控制。
  1. @Secured("ROLE_ADMIN")
  2. public void secureMethod() {
  3.     // 只有具有 ROLE_ADMIN 角色的用户可以访问
  4. }
  5. @PreAuthorize("hasRole('ADMIN')")
  6. public void preAuthorizeMethod() {
  7.     // 在方法调用之前进行授权检查
  8. }
复制代码
要使用注解的方式,必要使用 @EnableMethodSecurity 注解开启这一功能
  1. @Configuration
  2. @EnableWebSecurity
  3. @EnableMethodSecurity // 开启基于注解的方法级安全控制
  4. public class SecurityConfig {}
复制代码
除此之外,Spring Security 支持使用表达式语言 (SpEL) 来定义复杂的访问控制规则。
  1. @PreAuthorize("hasRole('ROLE_USER') and #id == principal.id")
  2. public void updateUser(Long id) {
  3.     // 只有具有 ROLE_USER 角色且 id 等于当前用户的 principal.id 的用户可以访问
  4. }
复制代码
4.4 事件监听

Spring Security 允许监听安全事件,例如登录成功、失败、登出等,以便记录日志或执行其他操作。
  1. @Component
  2. public class AuthenticationEventListener implements ApplicationListener<AuthenticationSuccessEvent> {
  3.     @Override
  4.     public void onApplicationEvent(AuthenticationSuccessEvent event) {
  5.         // 处理认证成功事件
  6.     }
  7. }
复制代码
五、工作原理

Spring Security 的计划有那么亿点复杂,我们通过分开描述其焦点工作流程、认证工作流程、鉴权工作流程来阐述其工作原理。
5.1 焦点工作流程

Spring Security 的焦点工作流程如下:

  • 用户发送请求:用户通过浏览器或客户端发送请求到应用步伐。
  • 请求进入过滤器链署理(FilterChainProxy):所有请求首先辈入 FilterChainProxy,它是一个标准的 Servlet 过滤器 (javax.servlet.Filter)。FilterChainProxy 的作用是根据请求的路径(URL)匹共同适的 SecurityFilterChain。
  • 请求进入安全过滤器链(SecurityFilterChain):匹配到相应的 SecurityFilterChain 后,请求进入该安全过滤器链。SecurityFilterChain 是由多个安全过滤器(SecurityFilter)构成的序列,每个安全过滤器按次序处理请求。
  • 执行安全过滤器(SecurityFilter):每个安全过滤器 (SecurityFilter) 负责执行特定的安全操作和战略,例如身份认证、授权、会话管理等。
  • 调用认证管理器(AuthenticationManager):AuthenticationManager 是 Spring Security 的焦点接口之一,负责处理认证请求。在认证过程中,AuthenticationManager 通常会使用 AuthenticationProvider 来举行具体的认证操作。
  • 返回认证结果:AuthenticationManager 返回认证结果给 SecurityFilter。
  • 返回过滤器链:认证完成后,SecurityFilter 可能会举行一些额外的安全处理,并将请求继续传递给下一个安全过滤器或者返回给 SecurityFilterChain。
  • 返回过滤器链署理:处理完所有安全过滤器后,请求终极返回到 FilterChainProxy。
  • 返回响应:FilterChainProxy 将终极的响应返回给用户,完成整个请求-响应周期。

FilterChainProxy


  • 作用:FilterChainProxy 是 Spring Security 中的一个焦点组件,它负责管理 Spring Security 中的各种过滤器链。当一个 HTTP 请求到达应用步伐时,FilterChainProxy 会将该请求传递给一个或多个 SecurityFilterChain 实例举行处理。如果必要,FilterChainProxy 还可以重定向请求或返回错误信息。
  • 计划目标:FilterChainProxy 的计划目标是提供一个统一的入口点,用于管理和协调 Spring Security 中的所有过滤器链。
  • 例子:假设我们正在开发一个 Web 应用步伐,该应用步伐有多个端点,如 /public、/user 和 /admin。我们可能盼望 /public 端点对所有人开放,/user 端点只对已登录用户开放,而 /admin 端点只对管理员开放。在这种情况下,你可以使用 FilterChainProxy 来管理三个不同的过滤器链,每个过滤器链负责一个特定的端点。
SecurityFilterChain


  • 作用:SecurityFilterChain 是一个顶层接口。SecurityFilterChain 和 Servlet 中的 FilterChain 一样,同样维护了很多 Filter,这些 Filter 由 Spring Security 提供,每个 Filter 具有不同的职能。
  • 计划目标:SecurityFilterChain 的计划目标是为了支持添加一个或多个 SecurityFilterChain,每个SecurityFilterChain 负责不同的请求(比如依据请求地点举行区分),这样可以为不同的请求设置不同的认证规则。
  • 例子:继续上面的例子,我们可能会为 /public 端点创建一个 SecurityFilterChain,该过滤器链包含一个查抄请求是否为 GET 的过滤器。对于 /user 和 /admin 端点,我们可能会创建包含身份验证过滤器的 SecurityFilterChain,该过滤器查抄用户是否已登录,并根据用户的角色(用户或管理员)授予相应的权限。
SecurityFilter


  • 作用:SecurityFilter 是 Spring Security 的过滤器,每个过滤器负责处理特定的安全使命。当请求到达应用步伐时,它会依次通过过滤器链中的每个过滤器,直到到达目标资源。在过滤器链中,每个过滤器都可以对请求举行拦截、修改或执行其他操作,以确保应用步伐的安全性。
  • 计划目标:SecurityFilter 的计划目标是为了提高 Web 应用步伐的安全性、可维护性和可扩展性。
  • 例子:在上述 SecurityFilterChain 中,我们可能会使用多个 SecurityFilter。例如,一个 SecurityFilter 可能会查抄请求是否为 GET,另一个 SecurityFilter 可能会查抄用户是否已登录,还有一个 SecurityFilter 可能会根据用户的角色授予相应的权限。
AuthenticationManager


  • 作用:AuthenticationManager 是 Spring Security 中的认证管理器,用来对登录请求举行处理。当处理用户的登录请求时,例如在使用表单登录时,AuthenticationManager 的 authenticate 方法会被调用来处理请求。
  • 计划目标:AuthenticationManager 这个接口的计划目标是对用户的未授信根据举行认证,认证通过则返回授信状态的根据,否则将抛出认证异常 AuthenticationException。
  • 例子:当用户尝试登录时,在登录表单中输入用户名和暗码,这些根据将被传递给 AuthenticationManager。AuthenticationManager 会查抄这些根据是否有效。如果根据有效,AuthenticationManager 将创建一个已认证的 Authentication 对象,该对象包含用户的详细信息和授权。如果根据无效,AuthenticationManager 将抛出一个异常。

5.2 认证工作流程

认证是验证用户身份的过程,通常通过用户名和暗码、数字证书或生物特征等手段举行。通过 Spring Security 的焦点工作流程我们可以知道:Spring Security 具体的认证工作是交由 AuthenticationManager 执行的
AuthenticationManager 的认证流程如下:

  • 用户提交凭据:用户向系统提交用户名和暗码等凭据信息。
  • 认证管理器 (AuthenticationManager):汲取到凭据后,AuthenticationManager 负责举行认证。
  • 认证提供者 (AuthenticationProvider):AuthenticationManager 调用认证方法,该方法会委托给配置的 AuthenticationProvider(认证提供者)。
  • 用户详情服务 (UserDetailsService):认证提供者通过调用 UserDetailsService 加载用户的详细信息,通常是根据用户名加载用户对象。
  • 用户详情 (UserDetails):UserDetailsService 返回一个实现了 UserDetails 接口的用户详情对象,此中包含了用户的详细信息和权限。
  • 创建认证信息 (Authentication):认证提供者使用 UserDetails 对象创建一个 Authentication 对象,表示成功的认证。
  • 安全上下文持有者 (SecurityContextHolder):创建的 Authentication 对象被存储到 SecurityContextHolder 中,以便后续的访问控制和安全操作使用。
  • 返回认证结果:终极,认证结果以 Authentication 对象的情势返回给用户,表示用户已经成功通过认证。

Spring Security 的认证流程看似蛮复杂的。其实,它的认证流程和我们常规的认证方式是类似的。


  • AuthenticationManager:不举行具体的认证处理,负责管理多个 AuthenticatonProvider,具体的认证交由可以或许处理当前认证的 AuthenticationProvider。
  • AuthenticationProvider:举行具体认证,通过调用 UserDetailService 从数据库或者其他地方加载用户信息。之后通过与用户提交的凭据信息举行匹配,若成功则生成 Authentication 对象,表示认证成功,反之,返回认证失败信息。
  • UserDetailsService:它的重要作用是根据用户名加载用户的详细信息。加载的具体逻辑一般由开发人员实现。
  • UserDetails:用于表示用户的基本身份和授权信息(代表一个用户)。
  • Authentication:用于表示用户在系统中的身份认证信息。具体来说,Authentication 接口重要用于封装认证过程中的关键信息,如认证的主体(Principal)、凭据(Credentials)、授权信息(Authorities)等。
  • SecurityContextHolder:重要用于存储和访问当前用户的 Authentication 对象,即表示当前用户身份认证信息的实例。

5.3 鉴权工作流程

鉴权是在确认用户身份后,决定用户是否有权访问特定资源或执行特定操作的过程。Spring Security 鉴权流程通常是在认证完成之后,即生成了 Authentication 对象之后举行的。其具体流程如下:

  • AuthorizationFilter 授权过滤器:通过认证的请求被送到 AuthorizationFilter,它是 Spring Security 中的一个过滤器,负责处理所有的请求,并开启鉴权过程。
  • 获取 Authentication 认证信息:通过 SecurityContextHolder 从当前的安全上下文中获取用户的认证信息 (Authentication),这包罗用户的身份凭据和权限信息。
  • 调用 AccessDecisionManager 访问决定管理器:AuthorizationFilter 调用 AccessDecisionManager 举行实际的访问决定。AccessDecisionManager 是一个焦点组件,负责确定是否允许用户访问请求的资源或操作。AccessDecisionManager 可能会使用多个 AccessDecisionVoter 举行投票。AccessDecisionVoter 是决定的实际执行者,根据用户的认证信息和访问请求,投票是否允许访问资源或执行操作。
  • 返回投票结果:每个 AccessDecisionVoter 根据自身的逻辑判断是否允许访问。投票结果将汇总给 AccessDecisionManager。
  • 返回访问决定结果:AccessDecisionManager 将所有 AccessDecisionVoter 的投票结果综合起来,终极决定是否允许用户访问请求的资源或操作。
  • 返回响应:AuthorizationFilter 将处理的结果返回给用户,响应用户的请求,这可能包罗成功的访问授权或者拒绝访问的信息。

其实,鉴权的逻辑照旧比力简单的,只是流程比力多,可以概括为:

  • 首先,AuthorizationFilter 获取登录用户的认证信息(Authentication)
  • 然后,AuthorizationFilter 调用 AccessDecisionManager 判断权限
六、前后端分离

在之前的快速入门中,我们发现 Spring Security 默认情况下是采用前后端不分离的方式举行认证,而现在我们的项目一般都是前后端分离的方式(即: 前端通过 RESTful API 与后端举行通信,后端负责处理认证和授权,而前端则通过获取后端返回的 JWT(JSON Web Token)来管理用户的身份验证和授权状态)。要实现这一需求,我们可以参考如下步调:

  • 添加依靠:首先,在 pom.xml 文件中添加 Spring Security 和 JWT 的依靠:
    1. <dependencies>
    2.   <dependency>
    3.     <groupId>org.springframework.boot</groupId>
    4.     <artifactId>spring-boot-starter-web</artifactId>
    5.   </dependency>
    6.   <dependency>
    7.     <groupId>org.springframework.boot</groupId>
    8.     <artifactId>spring-boot-starter-security</artifactId>
    9.   </dependency>
    10.   <!-- JWT 依赖 -->
    11.   <dependency>
    12.     <groupId>io.jsonwebtoken</groupId>
    13.     <artifactId>jjwt</artifactId>
    14.     <version>0.9.1</version> <!-- 根据需要选择合适的版本 -->
    15.   </dependency>
    16. </dependencies>
    17. <dependencyManagement>
    18.   <dependencies>
    19.     <dependency>
    20.       <groupId>org.springframework.boot</groupId>
    21.       <artifactId>spring-boot-dependencies</artifactId>
    22.       <version>2.7.5</version>
    23.       <type>pom</type>
    24.       <scope>import</scope>
    25.     </dependency>
    26.   </dependencies>
    27. </dependencyManagement>
    复制代码
  • 创建用户服务实现类:创建一个实现 UserDetailsService 接口的服务类,用于从数据库加载用户信息,并将其返回给 Spring Security 举行认证和授权。
    1. @Service
    2. public class UserDetailsServiceImpl implements UserDetailsService {
    3.     // 模拟从数据库中查找用户
    4.     @Override
    5.     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    6.         // 检查传入的用户名是否为"zs"
    7.         if (!"zs".equals(username)) {
    8.             // 如果用户名不是"zs",返回 null,表示没有找到对应的用户
    9.             return null;
    10.         }
    11.         // 创建并返回一个 UserDetails 对象,表示用户的详细信息
    12.         return User.builder()
    13.                 .username("zs") // 设置用户名为"zs"
    14.                 .password("$2a$16$RBoXNEqVxxtZ5l1QrJaMPub32Z8Q/e01tIG1Irs9ThxfXgeWxV1jq") // 设置加密后的密码
    15.                 .authorities("add") // 设置用户的权限为"add"
    16.                 .build();
    17.     }
    18. }
    复制代码
  • 创建 JWT 工具类: 创建一个 JWT 工具类来生成和验证 JWT。
    1. @Component
    2. public class JwtUtil {
    3.     @Value("${jwt.secret}")
    4.     private String secret;
    5.     @Value("${jwt.expiration}")
    6.     private long expiration;
    7.     // 从令牌中提取用户名
    8.     public String extractUsername(String token) {
    9.         return extractClaim(token, Claims::getSubject);
    10.     }
    11.     // 从令牌中提取过期时间
    12.     public Date extractExpiration(String token) {
    13.         return extractClaim(token, Claims::getExpiration);
    14.     }
    15.     // 提取令牌中的声明
    16.     public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
    17.         final Claims claims = extractAllClaims(token);
    18.         return claimsResolver.apply(claims);
    19.     }
    20.     // 解析令牌
    21.     private Claims extractAllClaims(String token) {
    22.         return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    23.     }
    24.     // 验证令牌是否过期
    25.     private Boolean isTokenExpired(String token) {
    26.         return extractExpiration(token).before(new Date());
    27.     }
    28.     // 生成令牌
    29.     public String generateToken(UserDetails userDetails) {
    30.         return Jwts.builder()
    31.         .setSubject(userDetails.getUsername())
    32.         .setIssuedAt(new Date())
    33.         .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
    34.         .signWith(SignatureAlgorithm.HS256, secret)
    35.         .compact();
    36.     }
    37.     // 验证令牌
    38.     public Boolean validateToken(String token, UserDetails userDetails) {
    39.         final String username = extractUsername(token);
    40.         return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    41.     }
    42. }
    复制代码
  • 配置 JWT 相干属性:在 application.properties 或 application.yml 中配置 JWT 的密钥和逾期时间:
    1. jwt: # 配置JWT相关的属性
    2.   secret: secretKey # 用于签名和验证JWT令牌的密钥
    3.   expiration: 86400 # JWT令牌的有效期,以秒为单位。这里设置为86400秒(即24小时)
    复制代码
  • 创建 JWT 认证过滤器:创建一个 JWT 认证过滤器来拦截每个请求,并验证 JWT。
    1. // 这个类继承自 OncePerRequestFilter,确保在每次请求时只调用一次过滤器
    2. @Component
    3. public class JwtRequestFilter extends OncePerRequestFilter {
    4.     // 注入JwtUtil工具类
    5.     @Resource
    6.     private JwtUtil jwtUtil;
    7.     // 注入UserDetailsServiceImpl类,用于加载用户详细信息
    8.     @Resource
    9.     private UserDetailsServiceImpl userDetailsService;
    10.     // 重写OncePerRequestFilter的doFilterInternal方法,用于处理每个HTTP请求
    11.     @Override
    12.     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
    13.     throws ServletException, IOException {
    14.         // 从请求头中获取Authorization信息
    15.         final String authorizationHeader = request.getHeader("Authorization");
    16.         // 初始化用户名和JWT令牌变量
    17.         String username = null;
    18.         String jwt = null;
    19.         // 检查Authorization头是否以"Bearer "开头
    20.         if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
    21.             // 提取JWT令牌(去掉"Bearer "部分)
    22.             jwt = authorizationHeader.substring(7);
    23.             // 使用jwtUtil从令牌中提取用户名
    24.             username = jwtUtil.extractUsername(jwt);
    25.         }
    26.         // 如果用户名存在且当前没有已认证的用户
    27.         if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
    28.             // 加载用户详细信息
    29.             UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
    30.             // 验证JWT令牌
    31.             if (jwtUtil.validateToken(jwt, userDetails)) {
    32.                 // 创建UsernamePasswordAuthenticationToken对象,包含用户详细信息和权限
    33.                 UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
    34.                 new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
    35.                 // 将认证信息设置到SecurityContextHolder中
    36.                 SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
    37.             }
    38.         }
    39.         // 继续过滤链,处理下一个过滤器或目标资源
    40.         chain.doFilter(request, response);
    41.     }
    42. }
    复制代码
  • 配置 Spring Security:开启 Spring Security 验证,配置 SecurityFilterChain。
    1. @Configuration
    2. @EnableWebSecurity
    3. public class SecurityConfig {
    4.     // 注入JwtRequestFilter对象,用于处理JWT认证
    5.     @Resource
    6.     private JwtRequestFilter jwtRequestFilter;
    7.     @Bean
    8.     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    9.         http.csrf().disable() // 禁用CSRF保护
    10.                 .authorizeRequests() // 配置请求授权
    11.                 .antMatchers("/login").permitAll() // 对于"/login"路径,允许所有请求(无需认证)
    12.                 .anyRequest().authenticated() // 对于所有其他请求,需要认证
    13.                 .and()
    14.                 // 在UsernamePasswordAuthenticationFilter之前添加JwtRequestFilter
    15.                 .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    16.         return http.build();
    17.     }
    18.     @Bean
    19.     public AuthenticationManager authenticationManager(
    20.             UserDetailsService userDetailsService,
    21.             PasswordEncoder passwordEncoder) {
    22.         // 创建一个DaoAuthenticationProvider
    23.         DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
    24.       
    25.         // 设置UserDetailsService
    26.         authenticationProvider.setUserDetailsService(userDetailsService);
    27.         
    28.         // 设置PasswordEncoder
    29.         authenticationProvider.setPasswordEncoder(passwordEncoder);
    30.         // 返回包含这个认证提供者的ProviderManager
    31.         return new ProviderManager(authenticationProvider);
    32.     }
    33.     @Bean
    34.     public BCryptPasswordEncoder passwordEncoder() {
    35.         // 创建一个强度为16的BCryptPasswordEncoder
    36.         return new BCryptPasswordEncoder(16);
    37.     }
    38. }
    复制代码
  • 创建认证接口和控制器:创建一个认证控制器来处理用户登录请求,并返回 JWT 给前端。
    1. @RestController
    2. public class AuthController {
    3.     @Resource
    4.     private AuthenticationManager authenticationManager;
    5.     @Resource
    6.     private JwtUtil jwtUtil;
    7.     @Resource
    8.     private UserDetailsServiceImpl userDetailsService;
    9.     @PostMapping("/login")
    10.     public ResponseEntity<String> createAuthenticationToken(@RequestBody LoginRequest loginRequest) {
    11.         // 创建一个未认证的UsernamePasswordAuthenticationToken对象
    12.         UsernamePasswordAuthenticationToken authenticationToken =
    13.                 UsernamePasswordAuthenticationToken.unauthenticated(loginRequest.getUsername(), loginRequest.getPassword());
    14.         // 调用AuthenticationManager的authenticate方法进行用户认证
    15.         authenticationManager.authenticate(authenticationToken);
    16.         // 加载用户详细信息
    17.         UserDetails userDetails = userDetailsService.loadUserByUsername(loginRequest.getUsername());
    18.         // 生成JWT令牌
    19.         String jwt = jwtUtil.generateToken(userDetails);
    20.         // 返回包含JWT令牌的响应
    21.         return ResponseEntity.ok(jwt);
    22.     }
    23. }
    复制代码
  • 测试效果
    首先,访问 localhost:8080/login 获取到 token

    然后,使用 token 访问 localhost:8080/test/hello

七、小结

Spring Security 提供了强大而灵活的安全办理方案,可以轻松集成到 Spring 应用步伐中。但是我们不难发现 Spring Security 计划得确实复杂了那么亿点点 (¬‿¬)。Spring Security 的使用门槛虽然较高,但是如果明确了它的原理便可以无缝与 Spring 结合使用,在一样平常的开发中可以极大的提高开发效率,加强应用的安全性。
保举阅读


  • 深入探究 Spring Boot Starter:从概念到实践
  • 深入理解 Java 中的 volatile 关键字
  • OAuth 2.0:现代应用步伐的授权标准
  • Spring 三级缓存
  • 深入相识 MyBatis 插件:定制化你的持久层框架

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

九天猎人

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表