java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring Security使用

Spring Security的应用案例讲解

作者:熙客

Spring Security支持多认证方式(如OAuth2、JWT),实现基于角色的授权与防护(CSRF、会话管理),无缝集成Spring生态,适用于构建微服务安全体系,本文给大家介绍Spring Security的基本使用示例,感兴趣的朋友跟随小编一起看看吧

Spring Security原理

Spring Security是一个强大的身份验证和访问控制框架,它提供了一套全面的安全解决方案,包括身份验证、授权、防止攻击等功能,用于保护Spring应用程序。它的设计理念是基于过滤器链(Filter Chain)和委托模式,通过一系列的过滤器来处理不同的安全功能。 

1. 过滤器链(Filter Chain): Spring Security通过过滤器链的方式来处理安全性。每个过滤器负责一个特定的安全功能,例如身份验证、授权、会话管理等。过滤器链是有序的,请求会依次通过这些过滤器,每个过滤器都有机会对请求进行处理。

2. 委托模式: Spring Security使用委托模式将不同的安全功能委托给不同的组件。例如,身份验证(Authentication)的实现被委托给AuthenticationManager,而授权(Authorization)的实现则被委托给AccessDecisionManager。

3. 安全上下文(SecurityContext): 安全上下文是一个存储当前用户的地方,可以通过SecurityContextHolder来访问。它包含了当前用户的身份验证信息(Authentication)以及其他与安全相关的信息。

官方文档

一、核心能力

1.1身份认证 (Authentication) - “你是谁?”

1.2授权 (Authorization) - “你能做什么?”

1.3防护常见攻击

1.4与其他技术无缝集成

1.5 能力边界

二、核心架构与原理

Spring Security 的核心设计理念非常清晰:在 Servlet 过滤器(Filter)层面,为每一个进入应用的 HTTP 请求提供一系列的身份认证(Authentication)和授权(Authorization)检查

它本质上是一个过滤器链,请求必须逐一通过这条链上的每个过滤器,才能最终访问到你的 Controller 中的资源。如果任何一个过滤器检查失败,请求就会被重定向、抛出异常或直接返回错误信息。

2.1 HTTP完整的请求过程

2.2 核心组成

2.2.1 过滤器链 (Filter Chain) - 心脏

这是 Spring Security 最核心的概念。整个安全机制都构建在 Servlet 规范定义的 Filter 之上。当一个 HTTP 请求到来时,它会经过一个由多个安全过滤器组成的链条。

核心过滤器(按典型顺序):

工作流程简化视图:
HTTP Request -> Filter1 -> Filter2 -> ... -> FilterSecurityInterceptor -> DispatcherServlet -> Your Controller

2.2.2 认证 (Authentication) 核心组件

认证数据流:
UsernamePasswordAuthenticationFilter -> 创建 UsernamePasswordAuthenticationToken (未认证) -> 调用 ProviderManager.authenticate() -> 委托给 DaoAuthenticationProvider -> 调用 UserDetailsService.loadUserByUsername() -> 获取 UserDetails -> 比较密码 -> 认证成功 -> 返回一个已认证的 Authentication 对象 -> 被过滤器设置到 SecurityContextHolder 中。

2.2.3 授权 (Authorization) 核心组件

授权数据流:
请求到达 FilterSecurityInterceptor -> 获取受保护资源的 ConfigAttribute -> 调用 AccessDecisionManager.decide() -> 轮询所有 AccessDecisionVoter.vote() -> 根据投票策略(如“一票否决”、“多数同意”)做出最终决定 -> 允许访问或抛出 AccessDeniedException -> 被上层的 ExceptionTranslationFilter 捕获处理。

三、基本使用示例

需求:SpringBoot整合Spring Security页面登陆,要求用户信息存入数据库,且密码加密存储,登录成功后返回JWT令牌用于后续请求认证;要求体现不同用户授予不同权限;要求必要的安全配置。

安全特性:

项目结构:

src/
├── main/
│   ├── java/com/example/demo/
│   │   ├── config/
│   │   │   ├── SecurityConfig.java
│   │   │   ├── JwtAuthenticationFilter.java
│   │   │   └── JwtUtil.java
│   │   ├── controller/
│   │   │   ├── AuthController.java
│   │   │   └── TestController.java
│   │   ├── entity/
│   │   │   ├── User.java
│   │   │   └── Role.java
│   │   ├── mapper/
│   │   │   └── UserMapper.java
│   │   ├── service/
│   │   │   ├── UserService.java
│   │   │   └── CustomUserDetailsService.java
│   │   └── DemoApplication.java
│   └── resources/
│       ├── application.properties
│       ├── schema.sql
│       └── mapper/UserMapper.xml

3.1 依赖配置 (pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
        <relativePath/>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>1.0.0</version>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.5</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.5</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.5</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
</project>

3.2 应用配置 (application.properties)

# 服务器端口
server.port=8080
# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/security_demo?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# MyBatis配置
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.example.demo.entity
# JWT密钥
jwt.secret=mySecretKey
jwt.expiration=86400

密钥配置:

# 使用足够长且复杂的密钥
jwt.secret=mySuperLongAndComplexSecretKeyThatIsHardToGuess123!

虽然代码中不直接体现,但部署时必须使用HTTPS,防止中间人攻击,加密整个通信通道

# 生产环境应强制使用HTTPS
server.ssl.enabled=true
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=password
server.ssl.key-store-type=PKCS12

3.3 数据库初始化 (schema.sql)

CREATE DATABASE IF NOT EXISTS security_demo;
USE security_demo;
CREATE TABLE IF NOT EXISTS users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(100) NOT NULL,
    enabled BOOLEAN NOT NULL DEFAULT TRUE
);
CREATE TABLE IF NOT EXISTS roles (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS user_roles (
    user_id INT NOT NULL,
    role_id INT NOT NULL,
    PRIMARY KEY (user_id, role_id),
    FOREIGN KEY (user_id) REFERENCES users(id),
    FOREIGN KEY (role_id) REFERENCES roles(id)
);
-- 插入角色数据
INSERT IGNORE INTO roles (name) VALUES ('ROLE_USER');
INSERT IGNORE INTO roles (name) VALUES ('ROLE_ADMIN');
-- 插入用户数据(密码使用BCrypt加密,原始密码均为"password")
INSERT IGNORE INTO users (username, password, enabled) VALUES 
('user', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', 1),
('admin', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', 1);
-- 分配角色
INSERT IGNORE INTO user_roles (user_id, role_id) VALUES 
(1, 1), -- user has ROLE_USER
(2, 2); -- admin has ROLE_ADMIN

3.4 实体类

// User.java
package com.example.demo.entity;
import java.util.List;
public class User {
    private Long id;
    private String username;
    private String password;
    private Boolean enabled;
    private List<Role> roles;
    // 构造方法、getter和setter
    public User() {}
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
    // 省略getter和setter
}
// Role.java
package com.example.demo.entity;
public class Role {
    private Long id;
    private String name;
    // 构造方法、getter和setter
    public Role() {}
    public Role(String name) {
        this.name = name;
    }
    // 省略getter和setter
}

3.5 MyBatis Mapper接口和XML

// UserMapper.java
package com.example.demo.mapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
    User findByUsername(String username);
    User findById(Long id);
}
<!-- UserMapper.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
    <resultMap id="userResultMap" type="User">
        <id property="id" column="id" />
        <result property="username" column="username" />
        <result property="password" column="password" />
        <result property="enabled" column="enabled" />
        <collection property="roles" ofType="Role">
            <id property="id" column="role_id" />
            <result property="name" column="role_name" />
        </collection>
    </resultMap>
    <select id="findByUsername" resultMap="userResultMap">
        SELECT u.*, r.id as role_id, r.name as role_name
        FROM users u
        LEFT JOIN user_roles ur ON u.id = ur.user_id
        LEFT JOIN roles r ON ur.role_id = r.id
        WHERE u.username = #{username}
    </select>
    <select id="findById" resultMap="userResultMap">
        SELECT u.*, r.id as role_id, r.name as role_name
        FROM users u
        LEFT JOIN user_roles ur ON u.id = ur.user_id
        LEFT JOIN roles r ON ur.role_id = r.id
        WHERE u.id = #{id}
    </select>
</mapper>

3.6 服务层

// CustomUserDetailsService.java
package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userMapper.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("用户不存在: " + username);
        }
        List<GrantedAuthority> authorities = user.getRoles().stream()
                .map(role -> new SimpleGrantedAuthority(role.getName()))
                .collect(Collectors.toList());
        return new org.springframework.security.core.userdetails.User(
                user.getUsername(), 
                user.getPassword(), 
                authorities);
    }
}
// UserService.java
package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    public User findByUsername(String username) {
        return userMapper.findByUsername(username);
    }
}

3.7 JWT工具类

package com.example.demo.config;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
 * JWT工具类 - 负责JWT令牌的生成、解析和验证
 * 
 * 安全特性说明:
 * 1. 使用HMAC-SHA512算法进行签名,确保令牌完整性
 * 2. 设置合理的过期时间,减少令牌泄露风险
 * 3. 从配置文件中读取密钥,便于管理和轮换
 * 4. 提供完整的异常处理,防止无效令牌导致系统异常
 */
@Component
public class JwtUtil {
    // 从配置文件中注入JWT密钥,生产环境应使用复杂且足够长的密钥
    @Value("${jwt.secret}")
    private String secret;
    // 从配置文件中注入JWT过期时间(秒)
    @Value("${jwt.expiration}")
    private long expiration;
    /**
     * 生成JWT令牌
     * 
     * 安全考虑:
     * 1. 只包含必要信息(用户名),不包含敏感数据
     * 2. 设置签发时间和过期时间,控制令牌有效期
     * 3. 使用强加密算法(HS512)进行签名
     * 
     * @param authentication Spring Security认证对象
     * @return JWT令牌字符串
     */
    public String generateToken(Authentication authentication) {
        // 从认证对象中获取用户信息
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        Date now = new Date();
        // 计算过期时间:当前时间 + 配置的过期时间(转换为毫秒)
        Date expiryDate = new Date(now.getTime() + expiration * 1000);
        // 构建JWT令牌
        return Jwts.builder()
                .setSubject(userDetails.getUsername()) // 设置主题(用户名)
                .setIssuedAt(now)                     // 设置签发时间
                .setExpiration(expiryDate)            // 设置过期时间
                .signWith(SignatureAlgorithm.HS512, secret) // 使用HS512算法和密钥签名
                .compact();                           // 生成紧凑的JWT字符串
    }
    /**
     * 从JWT令牌中提取用户名
     * 
     * 安全考虑:
     * 1. 验证签名确保令牌未被篡改
     * 2. 解析前不信任任何令牌内容
     * 
     * @param token JWT令牌
     * @return 用户名
     */
    public String getUsernameFromToken(String token) {
        // 解析JWT令牌,验证签名并获取声明(Claims)
        Claims claims = Jwts.parser()
                .setSigningKey(secret)                // 设置签名密钥
                .parseClaimsJws(token)                // 解析JWS(已签名的JWT)
                .getBody();                           // 获取有效负载(Payload)
        // 返回主题(用户名)
        return claims.getSubject();
    }
    /**
     * 验证JWT令牌的有效性
     * 
     * 安全考虑:
     * 1. 验证签名是否正确,防止伪造令牌
     * 2. 检查令牌是否过期
     * 3. 捕获所有可能异常,防止无效令牌导致系统异常
     * 
     * @param token JWT令牌
     * @return 令牌是否有效
     */
    public boolean validateToken(String token) {
        try {
            // 尝试解析令牌,如果成功则说明令牌有效
            Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
            return true;
        } catch (SignatureException ex) {
            // 签名不匹配 - 令牌可能被篡改
            // 记录日志但不抛出异常,避免信息泄露
        } catch (MalformedJwtException ex) {
            // 令牌格式错误 - 不是有效的JWT
        } catch (ExpiredJwtException ex) {
            // 令牌已过期 - 需要重新登录获取新令牌
        } catch (UnsupportedJwtException ex) {
            // 不支持的JWT令牌 - 可能使用了错误的算法
        } catch (IllegalArgumentException ex) {
            // JWT claims string is empty - 令牌为空
        }
        // 任何异常都意味着令牌无效
        return false;
    }
}

3.8 JWT认证过滤

package com.example.demo.config;
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.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
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;
/**
 * JWT认证过滤器 - 处理每个请求的JWT认证
 * 
 * 安全特性说明:
 * 1. 在每个请求前执行,确保所有请求都经过认证检查
 * 2. 从Authorization头中提取Bearer令牌
 * 3. 验证令牌有效性并设置安全上下文
 * 4. 即使认证失败也继续过滤器链,确保公共接口可访问
 */
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private UserDetailsService userDetailsService;
    /**
     * 过滤器核心方法 - 处理每个HTTP请求
     * 
     * 安全流程:
     * 1. 从请求中提取JWT令牌
     * 2. 验证令牌有效性
     * 3. 如果有效,从令牌中提取用户名并加载用户详情
     * 4. 设置安全上下文,供后续授权检查使用
     * 
     * @param request HTTP请求
     * @param response HTTP响应
     * @param filterChain 过滤器链
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    FilterChain filterChain) throws ServletException, IOException {
        try {
            // 从HTTP请求中获取JWT令牌
            String jwt = getJwtFromRequest(request);
            // 验证令牌是否存在且有效
            if (StringUtils.hasText(jwt) && jwtUtil.validateToken(jwt)) {
                // 从有效令牌中提取用户名
                String username = jwtUtil.getUsernameFromToken(jwt);
                // 从数据库加载用户详细信息(包括权限)
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                // 创建认证令牌,包含用户详情和权限
                UsernamePasswordAuthenticationToken authentication = 
                    new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                // 添加请求详情(如IP地址、会话ID等)
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                // 将认证信息设置到安全上下文中,供后续授权检查使用
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        } catch (Exception ex) {
            // 捕获所有异常,避免因认证问题导致请求失败
            // 记录错误日志但继续处理请求(某些接口可能允许匿名访问)
            logger.error("Could not set user authentication in security context", ex);
        }
        // 继续过滤器链处理(无论认证成功与否)
        filterChain.doFilter(request, response);
    }
    /**
     * 从HTTP请求中提取JWT令牌
     * 
     * 安全考虑:
     * 1. 只接受Bearer类型的认证头
     * 2. 移除"Bearer "前缀,获取纯令牌
     * 
     * @param request HTTP请求
     * @return JWT令牌或null(如果不存在)
     */
    private String getJwtFromRequest(HttpServletRequest request) {
        // 从Authorization头获取Bearer令牌
        String bearerToken = request.getHeader("Authorization");
        // 检查令牌是否存在且格式正确(以"Bearer "开头)
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            // 返回去掉"Bearer "前缀的纯令牌
            return bearerToken.substring(7);
        }
        // 没有找到有效令牌
        return null;
    }
}

3.9 Spring Security配置

package com.example.demo.config;
import org.springframework.beans.factory.annotation.Autowired;
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.builders.AuthenticationManagerBuilder;
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;
/**
 * Spring Security配置类 - 定义应用程序的安全策略
 * 
 * 安全特性说明:
 * 1. 使用无状态会话管理,适合RESTful API
 * 2. 配置密码编码器,确保密码安全存储
 * 3. 定义URL访问规则,实现基于角色的访问控制
 * 4. 集成JWT认证过滤器,替代默认的表单登录
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private CustomUserDetailsService userDetailsService;
    @Autowired
    private JwtUtil jwtUtil;
    /**
     * 密码编码器Bean - 用于密码加密和验证
     * 
     * 安全考虑:
     * 1. 使用BCrypt强哈希算法,自动处理盐值
     * 2. 适合密码存储,抵抗彩虹表攻击
     * 
     * @return BCrypt密码编码器实例
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    /**
     * 认证管理器Bean - 暴露给其他组件使用
     * 
     * 用途:
     * 1. 在AuthController中用于手动认证用户
     * 2. 可以被其他需要认证服务的组件使用
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    /**
     * 配置认证管理器 - 设置自定义用户详情服务和密码编码器
     * 
     * 安全流程:
     * 1. 使用自定义UserDetailsService从数据库加载用户信息
     * 2. 使用BCrypt密码编码器验证密码
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
    /**
     * 配置HTTP安全策略 - 核心安全配置方法
     * 
     * 安全策略:
     * 1. 禁用CORS和CSRF(因使用无状态JWT认证)
     * 2. 使用无状态会话管理
     * 3. 配置URL访问规则(基于角色)
     * 4. 添加JWT认证过滤器
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // 启用CORS并禁用CSRF(因使用JWT而非Cookie)
            .cors().and().csrf().disable()
            // 会话管理设置为无状态(不创建和使用HTTP会话)
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            // 配置请求授权规则
            .authorizeRequests()
                .antMatchers("/api/auth/**").permitAll()      // 认证接口允许匿名访问
                .antMatchers("/api/user/**").hasRole("USER")  // 用户接口需要USER角色
                .antMatchers("/api/admin/**").hasRole("ADMIN") // 管理员接口需要ADMIN角色
                .anyRequest().authenticated()                 // 其他所有请求需要认证
            .and();
        // 添加JWT认证过滤器到UsernamePasswordAuthenticationFilter之前
        http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }
    /**
     * 创建JWT认证过滤器Bean
     * 
     * 说明:
     * 1. 过滤器在每个请求前执行
     * 2. 负责提取和验证JWT令牌
     * 3. 设置安全上下文中的认证信息
     * 
     * @return JWT认证过滤器实例
     */
    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter();
    }
}

3.10 控制器

// AuthController.java
package com.example.demo.controller;
import com.example.demo.config.JwtUtil;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private UserService userService;
    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody Map<String, String> loginRequest) {
        String username = loginRequest.get("username");
        String password = loginRequest.get("password");
        Authentication authentication = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(username, password)
        );
        SecurityContextHolder.getContext().setAuthentication(authentication);
        String jwt = jwtUtil.generateToken(authentication);
        User user = userService.findByUsername(username);
        Map<String, Object> response = new HashMap<>();
        response.put("token", jwt);
        response.put("user", user);
        return ResponseEntity.ok(response);
    }
}
// TestController.java
package com.example.demo.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class TestController {
    @GetMapping("/user/test")
    @PreAuthorize("hasRole('USER')")
    public String userAccess() {
        return "用户内容";
    }
    @GetMapping("/admin/test")
    @PreAuthorize("hasRole('ADMIN')")
    public String adminAccess() {
        return "管理员内容";
    }
}

3.11 主应用类

// DemoApplication.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

3.12 测试

登录获取令牌:

POST http://localhost:8080/api/auth/login
Content-Type: application/json
{
  "username": "user",
  "password": "password"
}

访问用户API:

GET http://localhost:8080/api/user/test
Authorization: Bearer <your_token>

访问管理员API:

GET http://localhost:8080/api/admin/test
Authorization: Bearer <your_token>

到此这篇关于Spring Security的基本使用示例的文章就介绍到这了,更多相关Spring Security使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文