【springboot+vue项目(十三)】 Springboot整合Spring Security+JWT

本文介绍了SpringSecurity的身份认证流程,从过滤器链开始,详细阐述了UsernamePasswordAuthenticationFilter、AuthenticationManager、AuthenticationProvider、UserDetailsService和PasswordEncoder的角色。此外,还说明了如何在SpringBoot项目中配置SpringSecurity,并展示了UserDetailsService的实现。最后,讨论了SpringSecurity与JWT的整合,包括创建JWT工具类、认证过滤器和登录接口的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、知识准备 

(一)Servlet规范

根据Servlet规范:

        一个客户端请求Request在Servlet容器中需要经过Filter Chain中一些列Filter处理后才会获取到Web资源,

        而且响应Response也需要再次经过Filter Chain中的Filter处理后才能返回给客户端。Spring基于该Servlet规范,在Filter中接入安全机制来保证Web资源的安全。

(二) Spring Security 

Spring Security 是一个强大的安全框架,用于保护 Java 应用程序免受各种安全威胁。它提供了全面的安全服务,包括认证、授权、攻击防护等。

  • 认证:Spring Security 处理用户身份的验证。它可以配置各种身份验证机制,如表单登录、LDAP、OAuth 2.0 等。通过集成 JWT,Spring Security 可以利用 JWT 作为认证令牌来确认用户身份。

  • 授权:Spring Security 提供了灵活的授权机制,通过配置角色和权限,控制用户对不同资源的访问。它可以根据 JWT 中的声明(如角色)来进行授权决策。

  • 安全配置:Spring Security 提供了用于配置安全策略的工具,确保应用程序的安全需求得以实现。例如,配置哪些端点需要认证,哪些不需要,防止跨站请求伪造(CSRF)等。

  • 保护机制:Spring Security 提供了防御性保护机制,如防止常见的安全漏洞(如跨站脚本攻击、跨站请求伪造、点击劫持等)。

Spring Security主要就做这2件事:

                1:身份认证(谁在发起请求),

                2:身份授权(是否有权限访问资源)

      但是需要明确一点:

                FilterSecurityInterceptor主要做的是基于访问规则的身份授权。而身份认证是身份授权的前提,因此FilterSecurityInterceptor会在认证信息不存在时进行一次身份认证。正常认证流程是在其他优先级更高的过滤器完成的身份认证,当然二者的认证流程一致:

  • 通过AuthenticationManager获取当前请求的身份认证信息
  • 通过AccessDecisionManager决断特定访问规则的web资源能否被访问

(三) JWT (JSON Web Token)

JWT 是一种用于在网络应用环境中安全传输信息的标准。它是一种自包含的令牌格式,能够在网络通信中传递用户身份和相关权限。

  • 无状态的认证:JWT 是自包含的,它存储了所有必要的用户信息和声明。使用 JWT,服务器端不需要维护会话状态,从而提高了扩展性和简化了跨多个服务器的负载均衡。

  • 安全性:JWT 可以被签署和加密。签署确保了数据的完整性和来源的真实性,加密则保护了数据的隐私。通过对 JWT 进行签名,服务器能够验证令牌未被篡改。

  • 灵活的声明:JWT 可以包含自定义的声明(Claims),这些声明提供了有关用户权限、角色等信息。这使得 JWT 能够灵活地满足不同应用场景的需求。

  • 跨域认证:由于 JWT 是一种独立于服务器存储的令牌,它可以在不同的前端和后端系统之间传递,支持跨域和跨平台的认证需求。

 通过JWT进行用户认证与传统会话管理的方式相比,具有一些显著的不同。传统的会话管理通常依赖服务器端存储用户的会话状态,而JWT(JSON Web Token)是一种无状态的认证机制,令用户在每次请求时都携带认证信息。下面是详细的讲解:

1. 传统会话管理 vs. JWT

传统会话管理

  • 会话存储:当用户登录时,服务器生成一个会话(Session),并在服务器端存储该会话的状态信息。
  • 会话ID:服务器生成一个唯一的会话ID,并将其发送给客户端,通常存储在浏览器的cookie中。
  • 状态管理:每次请求时,客户端将会话ID发送到服务器,服务器使用会话ID查找并恢复会话状态,进行身份验证和授权。
  • 状态存储问题:会话信息存储在服务器端,可能会增加服务器的负担,尤其是在大规模用户访问时。

JWT

  • 无状态:JWT包含了所有的认证信息,服务器不需要存储会话状态,认证信息直接嵌入在JWT中。
  • Token生成:当用户登录成功时,服务器生成一个JWT,并将其发送给客户端。JWT通常包含用户的身份信息和一些元数据(如过期时间)。
  • Token验证:每次请求时,客户端将JWT发送到服务器(通常在HTTP头部的Authorization字段中)。服务器解密和验证JWT,检查其有效性(例如签名和过期时间)以进行身份认证。
  • 无状态优势:由于JWT是无状态的,服务器不需要维护会话信息,减少了存储负担,并使得应用更具可扩展性。

2. JWT的工作流程

1. 用户登录

  • 用户请求:用户向服务器发送包含用户名和密码的登录请求。
  • 服务器验证:服务器验证用户的凭据。如果凭据正确,则生成JWT。

2. 生成JWT

  • 构造JWT:JWT通常包含三个部分:
    • 头部(Header):指定JWT的类型(通常是JWT)和签名算法(如HS256)。
    • 负载(Payload):包含声明(Claims),如用户ID、角色和其他元数据。
    • 签名(Signature):对头部和负载进行加密,以防止篡改。
  • 发送Token:服务器将生成的JWT返回给客户端,客户端通常将其存储在本地存储(LocalStorage)或cookie中。

3. 客户端请求

  • 附带Token:每次客户端发送请求时,将JWT附加在HTTP头部(通常是Authorization字段中)或者作为请求参数。
  • 服务器验证:服务器接收到请求后,提取JWT并进行验证:
    • 解密签名:使用服务器的密钥解密JWT的签名部分,验证Token的完整性和真实性。
    • 检查过期:验证Token的过期时间,确保Token仍然有效。
    • 提取用户信息:从JWT中提取用户信息,并进行身份验证和授权。

4. 服务器响应

  • 验证成功:如果JWT有效,服务器根据用户的权限进行相应的操作,并返回数据给客户端。
  • 验证失败:如果JWT无效(如签名错误或过期),服务器会返回认证失败的响应,客户端需要重新登录。

(四)整合的作用

当 Spring Security 和 JWT 结合使用时,它们共同解决了以下问题:

  1. 安全认证和授权:Spring Security 负责处理认证和授权的配置和策略,JWT 提供了一个安全、无状态的令牌来传递用户信息和权限。

  2. 无状态会话管理:通过 JWT,无需在服务器端存储会话状态,从而简化了扩展和负载均衡。

  3. 增强的安全性:Spring Security 提供了强大的安全机制,JWT 通过签名和加密增强了数据的安全性。

  4. 灵活性和扩展性:结合 JWT 的灵活声明和 Spring Security 的配置能力,能够满足复杂的认证和授权需求。

        Spring Security 提供了一个全面的安全框架,而 JWT 提供了一种轻量级、无状态的认证机制。结合这两者可以实现高效且安全的认证和授权解决方案。

二、Spring Security 身份认证流程

在Spring Security中进行身份认证的流程如:

1、过滤器链(FilterChain)(内部组件,不需配置):SpringSecurity的过滤器链负责处理HTTP请求并进行身份认证。过滤器链是由一系列过滤器组成的链条,其中包括UsernamePasswordAuthenticationFilter等过滤器。过滤器链根据请求的URL和配置的规则来确定是否对请求进行身份认证处理。 

2、UsernamePasswordAuthenticationFilter(内部组件,不需配置)这个过滤器负责处理基于用户名和密码的身份认证请求。它从请求中获取用户名和密码,并将其封装成UsernamePasswordAuthenticationToken对象,然后将该对象传递给下一个组件。

3、AuthenticationManager(默认不需配置,也可以自定义):AuthenticationManager是SpringSecurity中的一个核心接口,负责管理身份认证。当UsernamePasswordAuthenticationFilter传递UsernamePasswordAuthenticationToken给AuthenticationManager时,它会委托给一个或多个AuthenticationProvider来完成具体的身份认证过程。

4、AuthenticationProvider(默认不需配置,也可以自定义):AuthenticationProvider是一个接口,它定义了对特定类型的身份认证进行处理的方法。常见的实现类是DaoAuthenticationProvider,它使用用户名和密码进行身份认证。AuthenticationProvider依赖于UserDetailsService来获取用户的详细信息,并使用PasswordEncoder来进行密码的比对。

 5、UserDetailsService(默认不需配置,也可以自定义)UserDetailsService是一个接口,用于加载用户的详细信息。它根据用户名从数据库或其他存储中加载用户信息,包括密码和权限等。你可以自定义实现该接口,或使用Spring Security提供的默认实现。

6、PasswordEncoder(默认不需配置,也可以自定义):PasswordEncoder是一个接口,用于对密码进行编码和比对。在身份认证过程中,AuthenticationProvider使用PasswordEncoder来比对用户提交的密码和数据库中存储的密码是否匹配。常见的实现类是BCryptPasswordEncoder,它使用bcrypt算法进行密码的加密和比对。

 7、Authentication(内部组件,不需配置):当身份认证成功时,AuthenticationProvider会创建一个已认证的Authentication对象,其中包含了用户的身份信息和权限信息。该对象将被封装到SecurityContextHolder中的安全上下文中,供后续的权限控制和访问控制使用。

 8、登录成功或失败处理器(默认不需配置,也可以自定义):根据认证结果,UsernamePasswordAuthenticationFilter会根据配置的登录成功和失败处理器,进行相应的处理。例如,可以进行重定向到指定的页面或返回认证失败的错误信息。

三、Spring Security 编码流程

Spring Boot项目中使用Spring Security的实现用户认证配置和实现(授权控制和其他安全需求后续再实现):

 1、添加Spring Security依赖:在项目的pom.xml文件中添加以下依赖项:

<!-- Spring Security -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

 2、创建Spring Security配置类:SecurityConfig 继承WebSecurityConfigurerAdapter来自定义安全策略,

  • 注入 UserDetailsService,UserDetailsServiceImpl实现UserDetailsService接口,用于加载用户认证信息;
  • 自定义PasswordEncoder组件实现密码策略:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

     // 注入 UserDetailsService,用于加载用户认证信息
    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests() // 开始配置请求授权
                .antMatchers("/admin/**").hasRole("ADMIN") // 访问 /admin/** 路径需要 ADMIN 角色
                .antMatchers("/user/**").hasAnyRole("ADMIN", "USER") // 访问 /user/** 路径需要 ADMIN 或 USER 角色
                .anyRequest().authenticated() // 其他所有请求需要认证
                .and()
            .formLogin() // 配置表单登录
                .loginPage("/login") // 设置自定义登录页面路径
                .permitAll() // 允许所有用户访问登录页面
                .and()
            .logout() // 配置注销功能
                .permitAll(); // 允许所有用户访问注销功能
    }
    
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService) // 配置用户信息服务
            .passwordEncoder(passwordEncoder()); // 配置密码编码器
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(); // 定义 BCryptPasswordEncoder 作为密码编码器
    }
}

3、认证机制,可以进一步定制UserDetailsService,自定义UserDetailsServiceImpl实现UserDetailsService(组件自带)接口:


@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 根据用户名查询用户信息
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserName, username);
        User user = userMapper.selectOne(queryWrapper);

        // 判断是否查到用户,如果没查到抛出异常
        if (user == null) {
            throw new UsernameNotFoundException("用户不存在");
        }

        // 返回用户信息
        return new LoginUser(user);
    }
}

 用户登录实体类 LoginUser


/**
 * 自定义用户实现类,提供给Spring Security使用
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginUser implements UserDetails {

    private User user; // 封装的用户实体

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        // 返回用户的权限,示例中暂时返回null
        return null;
    }

    @Override
    public String getPassword() {
        // 返回用户的密码
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        // 返回用户的用户名
        return user.getUserName();
    }

    @Override
    public boolean isAccountNonExpired() {
        // 账户是否过期,示例中始终返回true
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        // 账户是否被锁定,示例中始终返回true
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        // 凭证是否过期,示例中始终返回true
        return true;
    }

    @Override
    public boolean isEnabled() {
        // 账户是否启用,示例中始终返回true
        return true;
    }
}

4、创建登录页面控制器 LoginController 


@Controller
public class WebController {

    @GetMapping("/")
    public String home() {
        return "home";
    }

    @GetMapping("/login")
    public String login() {
        return "login";
    }
}

5、创建视图模板 (仅做演示,不是项目中代码

   src/main/resources/templates目录下创建两个视图模板:home.htmllogin.html

home.html: 

<!DOCTYPE html>
<html>
<head>
    <title>Home</title>
</head>
<body>
    <h1>Welcome to the Home Page!</h1>
    <a href="/logout">Logout</a>
</body>
</html>

 login.html:

<!DOCTYPE html>
<html>
<head>
    <title>Login</title>
</head>
<body>
    <h1>Login</h1>
    <form action="/login" method="post">
        <div>
            <label for="username">Username:</label>
            <input type="text" id="username" name="username" />
        </div>
        <div>
            <label for="password">Password:</label>
            <input type="password" id="password" name="password" />
        </div>
        <div>
            <button type="submit">Login</button>
        </div>
    </form>
</body>
</html>

  四、Spring Security整合JWT

Spring Security+JWT认证流程:

  1. 用户登录: 提交用户名和密码 -> Spring Security进行认证。
  2. JWT生成: 认证成功后生成JWT并返回给用户。
  3. 请求中的JWT验证: 用户在后续请求中提供JWT -> JwtAuthenticationFilter验证JWT。
  4. 用户身份设置: 如果JWT有效,设置用户的身份到Spring Security上下文。
  5. 权限控制: 根据配置的权限规则检查用户是否有权访问资源。

具体的编码过程 

  1. 添加依赖:首先,在 pom.xml 文件中添加 Spring Boot、Spring Security 和 JWT 相关的依赖。这些依赖提供了你所需的库和工具,用于构建安全性和处理 JWT。

  2. 配置 JWT:创建一个JwtUtil工具 类,这个类负责生成和解析 JWT。它使用一个秘密密钥来签署令牌,并设置令牌的有效期。你可以根据需求调整这些配置。

  3. 配置 Spring Security:在 SecurityConfig 类中配置 Spring Security,以便它知道如何处理 JWT。需要禁用 CSRF 保护(如果应用程序不需要它),并配置哪些端点需要认证,哪些不需要。同时,添加一个 JWT 过滤器来处理传入的请求并验证 JWT。

  4. 创建 JWT 过滤器JwtAuthenticationFilter拦截器用于从请求中提取 JWT 并验证其有效性。如果 JWT 合法,它将设置 Spring Security 的认证上下文,使得应用程序可以基于 JWT 的内容进行授权。

  5. 添加认证控制器:创建一个控制器 AuthController,用于处理用户的登录请求。这个控制器将验证用户凭证,并在凭证合法时生成并返回 JWT。

  6. 配置属性文件:在 application.yml 文件中配置 JWT 的秘密密钥和有效期,这些配置将用于生成和验证 JWT。

1、添加相关依赖

pom.xml文件中添加以下依赖:

<!-- Spring Boot Starter -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Spring Security -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<!-- JSON Web Token (JWT) -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

2、创建JWT工具类:

创建一个工具类JwtUtil,用于生成和解析JWT令牌。


/**
 * JWT工具类
 */
public class JwtUtil {

    //有效期为
    public static final Long JWT_TTL = 24*60 * 60 *1000L;// 60 * 60 *1000  一个小时
    //设置秘钥明文
    public static final String JWT_KEY = "sangeng";

    public static String getUUID(){
        String token = UUID.randomUUID().toString().replaceAll("-", "");
        return token;
    }
    
    /**
     * 生成jtw
     * @param subject token中要存放的数据(json格式)
     * @return
     */
    public static String createJWT(String subject) {
        JwtBuilder builder = getJwtBuilder(subject, null, getUUID());// 设置过期时间
        return builder.compact();
    }

    /**
     * 生成jtw
     * @param subject token中要存放的数据(json格式)
     * @param ttlMillis token超时时间
     * @return
     */
    public static String createJWT(String subject, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(subject, ttlMillis, 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()
                .setId(uuid)              //唯一的ID
                .setSubject(subject)   // 主题  可以是JSON数据
                .setIssuer("sg")     // 签发者
                .setIssuedAt(now)      // 签发时间
                .signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥
                .setExpiration(expDate);
    }

    /**
     * 创建token
     * @param id
     * @param subject
     * @param ttlMillis
     * @return
     */
    public static String createJWT(String id, String subject, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);// 设置过期时间
        return builder.compact();
    }

    public static void main(String[] args) throws Exception {
        String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJjYWM2ZDVhZi1mNjVlLTQ0MDAtYjcxMi0zYWEwOGIyOTIwYjQiLCJzdWIiOiJzZyIsImlzcyI6InNnIiwiaWF0IjoxNjM4MTA2NzEyLCJleHAiOjE2MzgxMTAzMTJ9.JVsSbkP94wuczb4QryQbAke3ysBDIL5ou8fWsbt_ebg";
        Claims claims = parseJWT(token);
        System.out.println(claims);
    }

    /**
     * 生成加密后的秘钥 secretKey
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }
    
    /**
     * 解析
     *
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Claims parseJWT(String jwt) throws Exception {
        SecretKey secretKey = generalKey();
        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(jwt)
                .getBody();
    }


}

3、配置Spring Security:

(1)禁用 CSRFhttp.csrf().disable(); 关闭了跨站请求伪造保护,通常在使用 token         认证时这样做。

(2)无状态会话

   http

   .sessionManagement()

   .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

         配置 Spring Security 不使用 session 来管理用户状态。

(3)访问控制:允许匿名访问特定的 URL,比如登录接口 (/SSOlogin/login)。

         其他所有请求都需要认证才能访问。

(4)异常处理:配置了自定义的 AuthenticationEntryPoint 和 AccessDeniedHandler

   用于处理认证和授权异常。

(5)JWT 过滤器:将自定义的 JwtAuthenticationTokenFilter 添加到 Spring Security

         过滤器链中,用于处理 JWT 认证。

(6)跨域支持http.cors(); 允许跨域请求。

(7)密码编码器:使用 BCryptPasswordEncoder 对密码进行加密和验证。

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // 注入 JwtAuthenticationFilter,用于处理 JWT 认证
    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    // 注入自定义的 AuthenticationEntryPoint,用于处理未认证的请求
    @Autowired
    AuthenticationEntryPoint authenticationEntryPoint;

    // 注入自定义的 AccessDeniedHandler,用于处理认证后的拒绝访问情况
    @Autowired
    AccessDeniedHandler accessDeniedHandler;

    // 创建一个认证管理器
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        // 返回 Spring Security 默认的 AuthenticationManager 实例
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                // 关闭 CSRF(跨站请求伪造)保护,通常在使用 Token 认证时关闭
                .csrf().disable()
                // 配置 Session 管理
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 设定 Session 为无状态,不会创建和使用 Session
                .and()
                .authorizeRequests()
                // 配置允许匿名访问的 URL
                .antMatchers("/SSOlogin/login").anonymous()
                .antMatchers("/SSOlogin/callback").anonymous()
                .antMatchers("/login").anonymous()
                // TODO 这里需要对注销接口进行权限控制,通常不允许匿名访问
                // 配置其余所有请求需要认证才能访问
                .anyRequest().authenticated();

        // 配置异常处理器
        http.exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint) // 未认证请求的处理器
                .accessDeniedHandler(accessDeniedHandler); // 已认证但访问被拒绝的处理器

        // 关闭默认的注销功能
        http.logout().disable();

        // 将自定义的 JWT 认证过滤器添加到 Spring Security 过滤器链中
        // 确保 JWT 过滤器在 UsernamePasswordAuthenticationFilter 之前执行
        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

        // 允许跨域请求
        http.cors();
    }

    // 对密码进行编码和验证,使用 BCrypt 算法
    @Bean
    public PasswordEncoder passwordEncoder() {
        // 返回一个 BCryptPasswordEncoder 实例
        return new BCryptPasswordEncoder();
    }
}

 说明:目前的这种实现方法

优点:

  1. 简单直接:配置相对简单,适用于基础的JWT认证。
  2. JWT专注:主要关注JWT过滤器的设置,对JWT进行解析和验证。

适用场景:

  • 项目只需要基本的JWT认证,不涉及复杂的用户角色和权限控制。
  • 需要快速搭建一个支持JWT认证的简单应用。

另外一种实现方法 ,后面用到授权的时候再进行使用。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasAnyRole("ADMIN", "USER")
                .anyRequest().authenticated()
                .and()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();

        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public JwtUtil jwtUtil() {
        return new JwtUtil();
    }

    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter();
    }
}

 优点:

  1. 更全面的用户管理:结合了 UserDetailsService  PasswordEncoder,支持更复杂的用户认证和密码管理。
  2. 细粒度的权限控制:允许配置不同的访问权限(如 /admin/** 和 /user/** 路径),适用于需要角色和权限管理的应用。

适用场景:

  • 项目需要复杂的用户权限管理,涉及不同角色的用户访问控制。
  • 需要自定义用户认证流程或密码加密机制。
  • 需要使用Spring Security的其他功能,如 UserDetailsService 来从数据库加载用户信息。

4、创建JWT认证过滤器:

          认证的实现主要通过JwtAuthenticationFilter过滤器来完成。这个过滤器通常会在请求到达控制器之前,从请求头中提取 JWT(JSON Web Token),然后验证它的有效性。具体步骤如下:

  1. JWT 过滤器JwtAuthenticationFilter 负责解析并验证 JWT。它检查请求中的 JWT 是否存在并有效。
  2. JWT 解析:解析 JWT 中的用户信息,比如用户名和角色。
  3. 设置上下文:将解析出的用户信息设置到 Spring Security 的上下文中,使得后续的安全检查能够识别当前用户的身份。
  4. 链式调用:将 JwtAuthenticationFilter 插入到 Spring Security 的过滤器链中,确保它在身份认证过程中正确处理请求。
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private ObjectMapper objectMapper; // 推荐使用 ObjectMapper 进行 JSON 处理

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 获取请求头中的 token
        String token = request.getHeader("token");
        if (!StringUtils.hasText(token)) {
            // 说明该接口不需要登录,直接放行
            filterChain.doFilter(request, response);
            return;
        }

        // 解析获取 userid
        Claims claims;
        try {
            claims = JwtUtil.parseJWT(token);
        } catch (Exception e) {
            // 打印异常日志并返回错误信息给客户端
            e.printStackTrace();
            ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.TOKEN_INVALID, "TOKEN令牌无效");
            WebUtils.renderString(response, objectMapper.writeValueAsString(result));
            return;
        }

        String userId = claims.getSubject();
        // 从 Redis 缓存中获取 JSON 字符串并反序列化为 User 对象
        String userJsonFromCache = redisCache.getCacheObject("userInfo:" + userId);
        User user = objectMapper.readValue(userJsonFromCache, User.class);

        // 如果获取不到
        if (Objects.isNull(user)) {
            // 说明登录过期,提示重新登录
            ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
            WebUtils.renderString(response, objectMapper.writeValueAsString(result));
            return;
        }

        // 存入 SecurityContextHolder
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user, null, null);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        filterChain.doFilter(request, response);
    }
}

5、创建登录接口:

创建一个登录接口AuthController,用于验证用户身份并生成JWT令牌。(仅作为示例,后面会进行整合Oauth2.0)

@RestController
public class AuthController {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtUtil jwtUtil;

    @PostMapping("/login")
    public ResponseEntity<?> createToken(@RequestBody AuthRequest authRequest) {
        authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword()));

        final String token = jwtUtil.generateToken(authRequest.getUsername());
        return ResponseEntity.ok(new AuthResponse(token));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值