Spring Security前后端分离接入流程保姆级教程

基 于 角 色 权 限 模 型 与 数 据 权 限 控 制

以 小 明 的 摄 影 作 品 管 理 系 统 为 例 , 从 零 基 础 搭 建 到 完 整 认 证 鉴 权 , 逐 步 演 示 Spring Security 的 接 入 流 程 。 涵 盖 基 础 接 入 、 角 色 权 限 扩 展 、 数 据 权 限 控 制 三 大 模 块 , 每 步 标 注 自 定 义 类 与 Spring Security 扩 展 点 , 配 代 码 、 注 释 和 逻 辑 说 明 。 使 用 MyBatisPlus 作 为 ORM 框 架 。

** 第 一 部 分 : 基 础 接 入 流 程 --- 前 后 端 分 离 认 证 鉴 权 **

** 第 一 步 : 环 境 准 备 与 依 赖 引 入 **

** 目 的 **

搭 建 Spring Boot 基 础 项 目 , 引 入 Spring Security 及 前 后 端 分 离 必 需 组 件 , 使 用 MyBatisPlus 替 代 JPA 。

** 操 作 **

  1. ** 创 建 Spring Boot 项 目 **

    通 过 start.spring.io 创 建 , 选 择 Maven 、 Java 17+ , 添 加 依 赖 Spring Web 、 Spring Security 、 MyBatis Plus 、 MySQL Driver 、 Lombok 。

  2. ** Pom.xml 核 心 依 赖 **

xml 复制代码
<dependencies>  
    <!-- Spring Web -->  
    <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>  

    <!-- MyBatis Plus -->  
    <dependency>  
        <groupId>com.baomidou</groupId>  
        <artifactId>mybatis-plus-boot-starter</artifactId>  
        <version>3.5.3.1</version>  
    </dependency>  

    <!-- MySQL驱动 -->  
    <dependency>  
        <groupId>com.mysql</groupId>  
        <artifactId>mysql-connector-j</artifactId>  
        <scope>runtime</scope>  
    </dependency>  

    <!-- JWT支持 -->  
    <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>  

    <!-- Lombok -->  
    <dependency>  
        <groupId>org.projectlombok</groupId>  
        <artifactId>lombok</artifactId>  
        <optional>true</optional>  
    </dependency>  
</dependencies>  

** 第 二 步 : 数 据 库 设 计 与 实 体 类 **

** 目 的 **

设 计 用 户 、 角 色 表 , 并 让 用 户 PO 类 实 现 Spring Security 的 UserDetails 接 口 。

** 操 作 **

  1. ** 数 据 库 表 设 计 **
sql 复制代码
CREATE TABLE sys_user (  
    id BIGINT AUTO_INCREMENT PRIMARY KEY,  
    username VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名',  
    password VARCHAR(100) NOT NULL COMMENT 'BCrypt加密密码',  
    email VARCHAR(100) COMMENT '邮箱',  
    enabled BOOLEAN DEFAULT TRUE COMMENT '账户是否启用'  
);  

CREATE TABLE sys_role (  
    id BIGINT AUTO_INCREMENT PRIMARY KEY,  
    role_name VARCHAR(50) NOT NULL UNIQUE COMMENT '角色名称(无ROLE_前缀)'  
);  

CREATE TABLE sys_user_role (  
    user_id BIGINT,  
    role_id BIGINT,  
    PRIMARY KEY (user_id, role_id),  
    FOREIGN KEY (user_id) REFERENCES sys_user(id),  
    FOREIGN KEY (role_id) REFERENCES sys_role(id)  
);  
  1. ** 实 体 类 UserPo 实 现 UserDetails 接 口 **
java 复制代码
import com.baomidou.mybatisplus.annotation.TableName;  
import lombok.Data;  
import org.springframework.security.core.GrantedAuthority;  
import org.springframework.security.core.authority.SimpleGrantedAuthority;  
import org.springframework.security.core.userdetails.UserDetails;  
import java.util.Collection;  
import java.util.Set;  
import java.util.stream.Collectors;  

@Data  
@TableName("sys_user")  
public class UserPo implements UserDetails {  
    private Long id;  
    private String username;  
    private String password;  
    private String email;  
    private Boolean enabled = true;  
    private Set<RolePo> roles;  

    @Override  
    public Collection<? extends GrantedAuthority> getAuthorities() {  
        return roles.stream()  
            .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getRoleName()))  
            .collect(Collectors.toList());  
    }  

    @Override  
    public boolean isAccountNonExpired() { return true; }  

    @Override  
    public boolean isAccountNonLocked() { return true; }  

    @Override  
    public boolean isCredentialsNonExpired() { return true; }  

    @Override  
    public boolean isEnabled() { return enabled; }  
}  
  1. ** 角 色 实 体 类 RolePo **
java 复制代码
import com.baomidou.mybatisplus.annotation.TableName;  
import lombok.Data;  

@Data  
@TableName("sys_role")  
public class RolePo {  
    private Long id;  
    private String roleName;  
}  

** 第 三 步 : MyBatisPlus Mapper 层 **

** 目 的 **

使 用 MyBatisPlus Mapper 操 作 数 据 库 。

** 操 作 **

  1. ** UserMapper 接 口 **
java 复制代码
import com.baomidou.mybatisplus.core.mapper.BaseMapper;  
import org.apache.ibatis.annotations.Param;  
import java.util.Optional;  

public interface UserMapper extends BaseMapper<UserPo> {  
    Optional<UserPo> findByUsername(@Param("username") String username);  
}  
  1. ** RoleMapper 接 口 **
java 复制代码
import com.baomidou.mybatisplus.core.mapper.BaseMapper;  
import org.apache.ibatis.annotations.Param;  

public interface RoleMapper extends BaseMapper<RolePo> {  
    Optional<RolePo> findByRoleName(@Param("roleName") String roleName);  
}  

** 第 四 步 : Service 层 --- 实 现 UserDetailsService **

** 目 的 **

实 现 Spring Security 的 UserDetailsService 接 口 , 配 置 密 码 编 码 器 。

** 操 作 **

  1. ** 自 定 义 UserDetailsService 实 现 类 **
java 复制代码
import lombok.RequiredArgsConstructor;  
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;  

@Service  
@RequiredArgsConstructor  
public class UserDetailsServiceImpl implements UserDetailsService {  

    private final UserMapper userMapper;  

    @Override  
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {  
        UserPo user = userMapper.findByUsername(username)  
            .orElseThrow(() -> new UsernameNotFoundException("用户不存在:" + username));  
        return user;  
    }  
}  
  1. ** 配 置 密 码 编 码 器 **
java 复制代码
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;  
import org.springframework.security.crypto.password.PasswordEncoder;  

@Configuration  
public class PasswordEncoderConfig {  

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

** 第 五 步 : Spring Security 配 置 类 **

** 目 的 **

配 置 认 证 规 则 、 授 权 规 则 、 JWT 过 滤 器 。

** 操 作 **

  1. ** 自 定 义 Security 配 置 类 **
java 复制代码
import lombok.RequiredArgsConstructor;  
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.configuration.AuthenticationConfiguration;  
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;  
import org.springframework.security.config.annotation.web.builders.HttpSecurity;  
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;  
import org.springframework.security.config.http.SessionCreationPolicy;  
import org.springframework.security.crypto.password.PasswordEncoder;  
import org.springframework.security.web.SecurityFilterChain;  
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;  

@Configuration  
@EnableWebSecurity  
@EnableGlobalMethodSecurity(prePostEnabled = true)  
@RequiredArgsConstructor  
public class SecurityConfig {  

    private final UserDetailsServiceImpl userDetailsService;  
    private final JwtAuthFilter jwtAuthFilter;  
    private final PasswordEncoder passwordEncoder;  

    @Bean  
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {  
        http.csrf(csrf -> csrf.disable())  
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))  
            .authorizeHttpRequests(auth -> auth  
                .requestMatchers("/api/auth/login", "/api/auth/register").permitAll()  
                .requestMatchers("/api/admin/**").hasRole("ADMIN")  
                .requestMatchers("/api/user/**").hasAnyRole("ADMIN", "USER")  
                .anyRequest().authenticated()  
            )  
            .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)  
            .userDetailsService(userDetailsService)  
            .passwordEncoder(passwordEncoder);  

        return http.build();  
    }  

    @Bean  
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {  
        return config.getAuthenticationManager();  
    }  
}  

** 第 六 步 : JWT 工 具 类 与 过 滤 器 **

** 目 的 **

实 现 JWT 生 成 、 验 证 、 提 取 用 户 名 , 并 通 过 过 滤 器 转 换 为 认 证 信 息 。

** 操 作 **

  1. ** JWT 工 具 类 JwtUtils **
java 复制代码
import io.jsonwebtoken.Claims;  
import io.jsonwebtoken.Jwts;  
import io.jsonwebtoken.SignatureAlgorithm;  
import org.springframework.beans.factory.annotation.Value;  
import org.springframework.security.core.userdetails.UserDetails;  
import org.springframework.stereotype.Component;  
import java.util.Date;  
import java.util.function.Function;  

@Component  
public class JwtUtils {  

    @Value("${app.jwt.secret}")  
    private String secret;  

    @Value("${app.jwt.expiration}")  
    private long expiration;  

    public String generateToken(UserDetails userDetails) {  
        return Jwts.builder()  
            .setSubject(userDetails.getUsername())  
            .setIssuedAt(new Date())  
            .setExpiration(new Date(System.currentTimeMillis() + expiration))  
            .signWith(SignatureAlgorithm.HS256, secret)  
            .compact();  
    }  

    public String extractUsername(String token) {  
        return extractClaim(token, Claims::getSubject);  
    }  

    public boolean validateToken(String token, UserDetails userDetails) {  
        final String username = extractUsername(token);  
        return (username.equals(userDetails.getUsername())) && !isTokenExpired(token);  
    }  

    private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {  
        final Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();  
        return claimsResolver.apply(claims);  
    }  

    private boolean isTokenExpired(String token) {  
        return extractClaim(token, Claims::getExpiration).before(new Date());  
    }  
}  
  1. ** JWT 认 证 过 滤 器 JwtAuthFilter **
java 复制代码
import lombok.RequiredArgsConstructor;  
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;  
import org.springframework.security.core.context.SecurityContextHolder;  
import org.springframework.security.core.userdetails.UserDetails;  
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;  
import org.springframework.stereotype.Component;  
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;  

@Component  
@RequiredArgsConstructor  
public class JwtAuthFilter extends OncePerRequestFilter {  

    private final JwtUtils jwtUtils;  
    private final UserDetailsServiceImpl userDetailsService;  

    @Override  
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)  
            throws ServletException, IOException {  
        try {  
            String jwt = parseJwt(request);  
            if (jwt != null && jwtUtils.validateToken(jwt)) {  
                String username = jwtUtils.extractUsername(jwt);  
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);  
                UsernamePasswordAuthenticationToken authentication =  
                    new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());  
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));  
                SecurityContextHolder.getContext().setAuthentication(authentication);  
            }  
        } catch (Exception e) {  
            logger.error("无法设置用户认证: {}", e);  
        }  

        filterChain.doFilter(request, response);  
    }  

    private String parseJwt(HttpServletRequest request) {  
        String headerAuth = request.getHeader("Authorization");  
        if (headerAuth != null && headerAuth.startsWith("Bearer ")) {  
            return headerAuth.substring(7);  
        }  
        return null;  
    }  
}  

** 第 七 步 : 登 录 接 口 与 资 源 接 口 **

** 目 的 **

编 写 登 录 接 口 认 证 并 生 成 Token , 编 写 受 保 护 资 源 接 口 。

** 操 作 **

  1. ** 登 录 控 制 器 AuthController **
java 复制代码
import lombok.RequiredArgsConstructor;  
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.userdetails.UserDetails;  
import org.springframework.web.bind.annotation.*;  

@RestController  
@RequestMapping("/api/auth")  
@RequiredArgsConstructor  
public class AuthController {  

    private final AuthenticationManager authenticationManager;  
    private final JwtUtils jwtUtils;  

    @PostMapping("/login")  
    public ResponseEntity<JwtResponse> login(@RequestBody LoginRequest loginRequest) {  
        Authentication authentication = authenticationManager.authenticate(  
            new UsernamePasswordAuthenticationToken(  
                loginRequest.getUsername(),  
                loginRequest.getPassword()  
            )  
        );  

        SecurityContextHolder.getContext().setAuthentication(authentication);  
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();  
        String jwt = jwtUtils.generateToken(userDetails);  

        return ResponseEntity.ok(new JwtResponse(jwt, userDetails.getUsername()));  
    }  
}  

** 第 二 部 分 : 权 限 表 扩 展 指 南 --- 基 于 角 色 权 限 模 型 **

** 一 、 数 据 库 扩 展 **

** 目 的 **

新 增 权 限 表 实 现 细 粒 度 权 限 控 制 。

** 操 作 **

  1. ** 新 增 权 限 表 结 构 **
sql 复制代码
CREATE TABLE sys_permission (  
    id BIGINT AUTO_INCREMENT PRIMARY KEY,  
    permission_symbol VARCHAR(100) NOT NULL UNIQUE COMMENT '权限符号(如 user:add)',  
    permission_name VARCHAR(100) NOT NULL COMMENT '权限名称'  
);  

CREATE TABLE sys_role_permission (  
    role_id BIGINT,  
    permission_id BIGINT,  
    PRIMARY KEY (role_id, permission_id),  
    FOREIGN KEY (role_id) REFERENCES sys_role(id),  
    FOREIGN KEY (permission_id) REFERENCES sys_permission(id)  
);  

** 二 、 实 体 类 扩 展 **

** 目 的 **

修 改 角 色 与 用 户 实 体 类 关 联 权 限 。

** 操 作 **

  1. ** 新 增 权 限 实 体 类 PermissionPo **
java 复制代码
import com.baomidou.mybatisplus.annotation.TableName;  
import lombok.Data;  
import java.util.Set;  

@Data  
@TableName("sys_permission")  
public class PermissionPo {  
    private Long id;  
    private String permissionSymbol;  
    private String permissionName;  
    private Set<RolePo> roles;  
}  
  1. ** 修 改 角 色 实 体 类 RolePo **
java 复制代码
import com.baomidou.mybatisplus.annotation.TableName;  
import lombok.Data;  
import java.util.Set;  

@Data  
@TableName("sys_role")  
public class RolePo {  
    private Long id;  
    private String roleName;  
    private Set<PermissionPo> permissions;  
    private Set<UserPo> users;  
}  
  1. ** 修 改 用 户 实 体 类 UserPo 权 限 加 载 **
java 复制代码
@Override  
public Collection<? extends GrantedAuthority> getAuthorities() {  
    Set<GrantedAuthority> authorities = new HashSet<>();  
    for (RolePo role : roles) {  
        authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleName()));  
        for (PermissionPo permission : role.getPermissions()) {  
            authorities.add(new SimpleGrantedAuthority(permission.getPermissionSymbol()));  
        }  
    }  
    return authorities;  
}  

** 三 、 MyBatisPlus Mapper 扩 展 **

** 目 的 **

添 加 权 限 相 关 Mapper 接 口 。

** 操 作 **

  1. ** PermissionMapper **
java 复制代码
import com.baomidou.mybatisplus.core.mapper.BaseMapper;  
import org.apache.ibatis.annotations.Param;  
import java.util.Optional;  

public interface PermissionMapper extends BaseMapper<PermissionPo> {  
    Optional<PermissionPo> findByPermissionSymbol(@Param("symbol") String symbol);  
}  

** 四 、 Service 层 扩 展 **

** 目 的 **

实 现 权 限 管 理 服 务 。

** 操 作 **

  1. ** 权 限 管 理 服 务 PermissionService **
java 复制代码
import lombok.RequiredArgsConstructor;  
import org.springframework.stereotype.Service;  
import org.springframework.transaction.annotation.Transactional;  
import java.util.Arrays;  
import java.util.stream.Collectors;  

@Service  
@RequiredArgsConstructor  
public class PermissionService {  

    private final PermissionMapper permissionMapper;  
    private final RoleMapper roleMapper;  

    @Transactional  
    public void assignPermissionToRole(Long roleId, String permissionSymbol) {  
        RolePo role = roleMapper.selectById(roleId);  
        PermissionPo permission = permissionMapper.findByPermissionSymbol(permissionSymbol)  
            .orElseGet(() -> {  
                PermissionPo p = new PermissionPo();  
                p.setPermissionSymbol(permissionSymbol);  
                p.setPermissionName(parsePermissionName(permissionSymbol));  
                permissionMapper.insert(p);  
                return p;  
            });  

        role.getPermissions().add(permission);  
        roleMapper.updateById(role);  
    }  

    private String parsePermissionName(String symbol) {  
        return Arrays.stream(symbol.split(":"))  
            .map(word -> word.substring(0, 1).toUpperCase() + word.substring(1))  
            .collect(Collectors.joining(" "));  
    }  
}  

** 五 、 控 制 器 层 扩 展 --- 使 用 权 限 注 解 **

** 目 的 **

通 过 @PreAuthorize 注 解 控 制 方 法 权 限 。

** 操 作 **

  1. ** 用 户 控 制 器 使 用 权 限 注 解 **
java 复制代码
import org.springframework.security.access.prepost.PreAuthorize;  
import org.springframework.web.bind.annotation.*;  

@RestController  
@RequestMapping("/api/users")  
public class UserController {  

    @PostMapping  
    @PreAuthorize("hasAuthority('user:add')")  
    public ResponseEntity<?> addUser() {  
        return ResponseEntity.ok("添加用户");  
    }  

    @DeleteMapping("/{id}")  
    @PreAuthorize("hasAuthority('user:delete')")  
    public ResponseEntity<?> deleteUser(@PathVariable Long id) {  
        return ResponseEntity.ok("删除用户");  
    }  
}  

** 第 三 部 分 : 数 据 权 限 控 制 --- 订 单 查 询 只 查 看 自 己 创 建 的 订 单 **

** 一 、 数 据 库 扩 展 --- 订 单 表 **

** 目 的 **

设 计 订 单 表 关 联 创 建 人 。

** 操 作 **

  1. ** 订 单 表 结 构 **
sql 复制代码
CREATE TABLE sys_order (  
    id BIGINT AUTO_INCREMENT PRIMARY KEY,  
    order_no VARCHAR(50) NOT NULL COMMENT '订单编号',  
    amount DECIMAL(10,2) NOT NULL COMMENT '订单金额',  
    creator_id BIGINT NOT NULL COMMENT '创建人ID',  
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,  
    FOREIGN KEY (creator_id) REFERENCES sys_user(id)  
);  
  1. ** 订 单 实 体 类 OrderPo **
java 复制代码
import com.baomidou.mybatisplus.annotation.TableName;  
import lombok.Data;  
import java.math.BigDecimal;  
import java.time.LocalDateTime;  

@Data  
@TableName("sys_order")  
public class OrderPo {  
    private Long id;  
    private String orderNo;  
    private BigDecimal amount;  
    private Long creatorId;  
    private LocalDateTime createdAt;  
}  

** 二 、 MyBatisPlus Mapper 与 Service **

** 目 的 **

实 现 订 单 数 据 访 问 与 权 限 过 滤 。

** 操 作 **

  1. ** OrderMapper **
java 复制代码
import com.baomidou.mybatisplus.core.mapper.BaseMapper;  
import org.apache.ibatis.annotations.Param;  
import java.util.List;  

public interface OrderMapper extends BaseMapper<OrderPo> {  
    List<OrderPo> findByCreatorId(@Param("creatorId") Long creatorId);  
}  
  1. ** 订 单 服 务 实 现 **
java 复制代码
import lombok.RequiredArgsConstructor;  
import org.springframework.security.core.context.SecurityContextHolder;  
import org.springframework.stereotype.Service;  
import java.util.List;  

@Service  
@RequiredArgsConstructor  
public class OrderServiceImpl implements OrderService {  

    private final OrderMapper orderMapper;  
    private final UserMapper userMapper;  

    @Override  
    public List<OrderPo> findMyOrders() {  
        UserPo currentUser = getCurrentUser();  
        return orderMapper.findByCreatorId(currentUser.getId());  
    }  

    @Override  
    public OrderPo findOrderById(Long id) {  
        OrderPo order = orderMapper.selectById(id);  
        UserPo currentUser = getCurrentUser();  

        if (!order.getCreatorId().equals(currentUser.getId())) {  
            throw new AccessDeniedException("无权访问此订单");  
        }  
        return order;  
    }  

    private UserPo getCurrentUser() {  
        String username = SecurityContextHolder.getContext().getAuthentication().getName();  
        return userMapper.findByUsername(username).orElseThrow();  
    }  
}  

** 三 、 控 制 器 层 实 现 **

** 目 的 **

编 写 订 单 相 关 接 口 , 集 成 数 据 权 限 控 制 。

** 操 作 **

  1. ** 订 单 控 制 器 **
java 复制代码
import org.springframework.security.access.prepost.PreAuthorize;  
import org.springframework.web.bind.annotation.*;  

@RestController  
@RequestMapping("/api/orders")  
@RequiredArgsConstructor  
public class OrderController {  

    private final OrderService orderService;  

    @GetMapping  
    @PreAuthorize("hasAuthority('order:view')")  
    public ResponseEntity<List<OrderPo>> getMyOrders() {  
        List<OrderPo> orders = orderService.findMyOrders();  
        return ResponseEntity.ok(orders);  
    }  

    @GetMapping("/{id}")  
    @PreAuthorize("hasAuthority('order:view')")  
    public ResponseEntity<OrderPo> getOrderById(@PathVariable Long id) {  
        OrderPo order = orderService.findOrderById(id);  
        return ResponseEntity.ok(order);  
    }  
}  

** 总 结 **

通 过 以 上 三 部 分 整 合 , 我 们 实 现 了 Spring Security 前 后 端 分 离 接 入 、 角 色 权 限 扩 展 、 数 据 权 限 控 制 的 完 整 流 程 。 核 心 要 点 如 下 :

  1. ** 基 础 接 入 ** : 实 现 用 户 认 证 与 JWT 授 权 。
  2. ** 权 限 扩 展 ** : 通 过 权 限 表 与 角 色 关 联 , 实 现 细 粒 度 权 限 控 制 。
  3. ** 数 据 权 限 ** : 在 Service 层 手 动 过 滤 数 据 , 确 保 用 户 只 能 查 看 自 己 创 建 的 订 单 。

整 个 系 统 兼 顾 了 安 全 性 与 灵 活 性 , 适 用 于 中 小 型 项 目 的 权 限 管 理 需 求 。

相关推荐
佛祖让我来巡山17 小时前
Spring Security 认证流程闭环与调用链路详解
springsecurity·authenticationmanager
佛祖让我来巡山1 天前
小明的Spring Security入门到深入实战
springsecurity
佛祖让我来巡山2 天前
⚠️登录认证功能的成长过程:整体概述
安全·登录·springsecurity·登录认证·认证授权
码熔burning3 个月前
Spring Security 深度学习(六): RESTful API 安全与 JWT
安全·spring·restful·springsecurity
A尘埃3 个月前
SpringSecurity版本的不同配置
认证·springsecurity·安全配置·不同版本
世纪摆渡人4 个月前
SpringSecurity-SpringSecurity入门介绍
springsecurity
1.01^10004 个月前
[7-01-01].第01章:安全与权限 - 核心概念
springsecurity
xiezhr6 个月前
SpringBoot3整合SpringSecurity6(五)自定义登陆页面
java·spring·springboot3·springsecurity
测试工程喵6 个月前
Token类型与用途详解:数字身份的安全载体图谱
功能测试·安全·接口测试·模块测试·token·登录认证·token类型