第四章 【若依框架:权限控制】Spring Security权限控制体系

目录

[一、Spring Security核心概念](#一、Spring Security核心概念)

[1. 认证(Authentication)](#1. 认证(Authentication))

[2. 授权(Authorization)](#2. 授权(Authorization))

二、Security核心配置

三、用户登录认证流程

[1. 前端登录流程](#1. 前端登录流程)

[2. 后端认证流程](#2. 后端认证流程)

四、权限数据加载机制

[1. 用户权限获取](#1. 用户权限获取)

[2. 前端权限指令](#2. 前端权限指令)

五、动态菜单路由加载

[1. 菜单树构建](#1. 菜单树构建)

[2. 前端路由处理](#2. 前端路由处理)

六、权限注解使用详解

[1. @PreAuthorize注解使用](#1. @PreAuthorize注解使用)

[2. 编程式权限验证](#2. 编程式权限验证)

[3. 公开接口配置](#3. 公开接口配置)


本文介绍了SpringSecurity的核心概念和实现方案。主要内容包括:

1)认证与授权机制,支持多种认证方式和基于角色的访问控制;

2)安全配置示例,展示如何配置CSRF防护、会话管理和权限规则;

3)完整的用户登录流程实现,从前后端交互到Token生成;

4)权限数据加载机制,包括菜单权限和角色权限的获取方式;

5)动态菜单路由的构建与前端处理;

6)权限注解的使用方法,包括@PreAuthorize注解和编程式权限验证。文中提供了详细的代码示例,涵盖了从基础配置到高级权限控制的完整解决方案。

一、Spring Security核心概念

1. 认证(Authentication)
  • 验证用户身份("你是谁")

  • 支持多种认证方式:用户名密码、OAuth2、JWT等

2. 授权(Authorization)
  • 验证用户权限("你能做什么")

  • 基于角色和权限的访问控制

二、Security核心配置

java 复制代码
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private UserDetailsService userDetailsService;
    
    @Autowired
    private JwtAuthenticationTokenFilter authenticationTokenFilter;
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeRequests()
            .antMatchers("/login", "/register", "/captchaImage").permitAll()
            .antMatchers(HttpMethod.GET, "/**/*.css", "/**/*.js").permitAll()
            .antMatchers("/swagger-ui.html", "/druid/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }
    
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(bCryptPasswordEncoder());
    }
}

三、用户登录认证流程

1. 前端登录流程
java 复制代码
login.vue → store/user.js → api/login.js → 发送请求
2. 后端认证流程
java 复制代码
SysLoginController → SysLoginService → UserDetailsServiceImpl → 生成Token

核心代码实现

java 复制代码
@Service
public class SysLoginService {
    
    public String login(String username, String password, String code) {
        // 1. 验证码校验
        validateCaptcha(code);
        
        // 2. 用户身份验证
        Authentication authentication = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(username, password)
        );
        
        // 3. 创建登录用户对象
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        
        // 4. 记录登录日志
        recordLoginLog(username, Constants.LOGIN_SUCCESS, "登录成功");
        
        // 5. 生成JWT Token
        return tokenService.createToken(loginUser);
    }
}

四、权限数据加载机制

1. 用户权限获取
java 复制代码
@Service
public class SysPermissionService {
    
    public Set<String> getMenuPermission(LoginUser loginUser) {
        if (loginUser.getUser().isAdmin()) {
            // 管理员拥有所有权限
            return Collections.singleton("*:*:*");
        }
        
        // 查询用户菜单权限
        return menuService.selectMenuPermsByUserId(loginUser.getUser().getUserId());
    }
    
    public Set<String> getRolePermission(LoginUser loginUser) {
        // 查询用户角色
        return roleService.selectRolePermissionByUserId(loginUser.getUser().getUserId());
    }
}
2. 前端权限指令
html 复制代码
<!-- 权限字符串控制 -->
<el-button v-hasPermi="['system:user:add']">新增用户</el-button>
<el-button v-hasPermi="['system:user:edit', 'system:user:update']">编辑</el-button>

<!-- 角色控制 -->
<el-button v-hasRole="['admin']">管理员功能</el-button>
<el-button v-hasRole="['user', 'editor']">编辑功能</el-button>

五、动态菜单路由加载

1. 菜单树构建
java 复制代码
@Service
public class SysMenuServiceImpl implements ISysMenuService {
    
    public List<SysMenu> selectMenuTreeByUserId(Long userId) {
        List<SysMenu> menus;
        if (SecurityUtils.isAdmin(userId)) {
            menus = menuMapper.selectMenuTreeAll();
        } else {
            menus = menuMapper.selectMenuTreeByUserId(userId);
        }
        return getChildPerms(menus, 0);
    }
    
    private List<SysMenu> getChildPerms(List<SysMenu> list, int parentId) {
        List<SysMenu> returnList = new ArrayList<>();
        for (SysMenu menu : list) {
            if (menu.getParentId() == parentId) {
                recursionFn(list, menu);
                returnList.add(menu);
            }
        }
        return returnList;
    }
}
2. 前端路由处理
javascript 复制代码
// permission.js 路由守卫
router.beforeEach(async (to, from, next) => {
    // 获取用户信息
    const hasToken = getToken();
    if (hasToken) {
        if (to.path === '/login') {
            next({ path: '/' });
        } else {
            const hasRoles = store.getters.roles && store.getters.roles.length > 0;
            if (hasRoles) {
                next();
            } else {
                try {
                    // 获取用户信息
                    const { roles } = await store.dispatch('user/getInfo');
                    // 生成动态路由
                    const accessRoutes = await store.dispatch('permission/generateRoutes', roles);
                    // 添加路由
                    router.addRoutes(accessRoutes);
                    next({ ...to, replace: true });
                } catch (error) {
                    await store.dispatch('user/resetToken');
                    next(`/login?redirect=${to.path}`);
                }
            }
        }
    }
});

六、权限注解使用详解

1. @PreAuthorize注解使用
java 复制代码
@RestController
@RequestMapping("/system/user")
public class SysUserController {
    
    // 验证单个权限
    @PreAuthorize("@ss.hasPermi('system:user:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysUser user) {
        // ...
    }
    
    // 验证多个权限中的任意一个
    @PreAuthorize("@ss.hasAnyPermi('system:user:add,system:user:edit')")
    @PostMapping
    public AjaxResult add(@RequestBody SysUser user) {
        // ...
    }
    
    // 验证角色
    @PreAuthorize("@ss.hasRole('admin')")
    @DeleteMapping("/{userIds}")
    public AjaxResult remove(@PathVariable Long[] userIds) {
        // ...
    }
}
2. 编程式权限验证
java 复制代码
// 检查权限
if (SecurityUtils.hasPermi("sys:user:edit")) {
    System.out.println("当前用户有编辑权限");
}

// 检查角色
if (SecurityUtils.hasRole("admin")) {
    System.out.println("当前用户是管理员");
}
3. 公开接口配置
java 复制代码
// 使用@Anonymous注解标记公开接口
@Anonymous
@GetMapping("/public/list")
public List<SomeData> publicList() {
    return someService.listPublicData();
}
相关推荐
青云计划11 小时前
知光项目知文发布模块
java·后端·spring·mybatis
赶路人儿11 小时前
Jsoniter(java版本)使用介绍
java·开发语言
Victor35611 小时前
MongoDB(9)什么是MongoDB的副本集(Replica Set)?
后端
Victor35611 小时前
MongoDB(8)什么是聚合(Aggregation)?
后端
探路者继续奋斗12 小时前
IDD意图驱动开发之意图规格说明书
java·规格说明书·开发规范·意图驱动开发·idd
消失的旧时光-194312 小时前
第十九课:为什么要引入消息队列?——异步系统设计思想
java·开发语言
yeyeye11113 小时前
Spring Cloud Data Flow 简介
后端·spring·spring cloud
A懿轩A13 小时前
【Java 基础编程】Java 面向对象入门:类与对象、构造器、this 关键字,小白也能写 OOP
java·开发语言
Tony Bai13 小时前
告别 Flaky Tests:Go 官方拟引入 testing/nettest,重塑内存网络测试标准
开发语言·网络·后端·golang·php
乐观勇敢坚强的老彭13 小时前
c++寒假营day03
java·开发语言·c++