第四章 【若依框架:权限控制】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();
}
相关推荐
用户685453759776914 分钟前
同步成本换并行度:多线程、协程、分片、MapReduce 怎么选才不踩坑
后端
javaTodo22 分钟前
Claude Code 记忆机制详解:从 CLAUDE.md 到 Auto Memory,六层体系全拆解
后端
LSTM9743 分钟前
使用 C# 和 Spire.PDF 从 HTML 模板生成 PDF 的实用指南
后端
JaguarJack1 小时前
为什么 PHP 闭包要加 static?
后端·php·服务端
BingoGo1 小时前
为什么 PHP 闭包要加 static?
后端
是糖糖啊1 小时前
OpenClaw 从零到一实战指南(飞书接入)
前端·人工智能·后端
百度Geek说1 小时前
基于Spark的配置化离线反作弊系统
后端
后端AI实验室2 小时前
用AI写代码,我差点把漏洞发上线:血泪总结的10个教训
java·ai
Java编程爱好者2 小时前
虚拟线程深度解析:轻量并发编程的未来趋势
后端