Spring Security + JWT的权限认证框架

最近需要使用Security认证框架,网上也有挺多相关文章,结合大家的分享加上一些我自己的见解,搭建了一个比较完整的dome,有需要的可以到git上拷贝。

git地址:https://gitee.com/wnmemory/security_demo.git

RBAC 模型

RBAC(Role-Based Access Control,基于角色的访问控制)是当前最流行的权限管理模型之一。它通过“用户 → 角色 → 权限”的映射关系,简化权限分配、降低管理成本,并支持灵活扩展。

组成说明举例
用户(User)系统中的主体,通常是登录账户张三、李四
角色(Role)一组权限的集合,代表某种身份或职责管理员、普通用户、财务
权限(Permission)对资源的操作许可删除订单、查看报表、添加用户
资源(Resource)被访问的对象订单、报表、用户菜单
RBAC 关键设计表结构
CREATE TABLE `user` (
  `id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户ID,主键',
  `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户名,唯一',
  `password` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '密码',
  `phone` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '手机号,唯一',
  `nickname` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '昵称',
  `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '头像URL',
  `gender` tinyint DEFAULT NULL COMMENT '性别:0-未知,1-男,2-女',
  `status` tinyint DEFAULT '1' COMMENT '状态:0-禁用,1-正常',
  `is_deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除标志:0-未删除,1-已删除',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`),
  UNIQUE KEY `phone` (`phone`),
  KEY `idx_username` (`username`),
  KEY `idx_phone` (`phone`),
  KEY `idx_is_deleted` (`is_deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';
CREATE TABLE `role` (
  `id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色ID,主键,使用字符串形式',
  `role` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色名称',
  `role_code` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色编码,唯一',
  `status` tinyint DEFAULT '1' COMMENT '状态:0-禁用,1-启用',
  `created_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人账号',
  `updated_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '修改人账号',
  `is_deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除标志:0-未删除,1-已删除',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_role_code` (`role_code`),
  KEY `idx_role` (`role`),
  KEY `idx_status` (`status`),
  KEY `idx_created_by` (`created_by`),
  KEY `idx_updated_by` (`updated_by`),
  KEY `idx_is_deleted` (`is_deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色表';
CREATE TABLE `user_role` (
  `id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '映射ID,主键,使用字符串形式',
  `user_id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户ID,外键关联user表',
  `role_id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色ID,外键关联role表',
  `status` tinyint DEFAULT '1' COMMENT '状态:0-禁用,1-启用',
  `is_deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除标志:0-未删除,1-已删除',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_user_role` (`user_id`,`role_id`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_role_id` (`role_id`),
  KEY `idx_status` (`status`),
  KEY `idx_is_deleted` (`is_deleted`),
  CONSTRAINT `fk_user_role_role` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`),
  CONSTRAINT `fk_user_role_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户-角色映射表';
CREATE TABLE `permission` (
  `id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '权限ID,主键,使用字符串形式',
  `code` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '权限代码,唯一',
  `name` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '权限名称',
  `description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '权限描述',
  `is_deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除标志:0-未删除,1-已删除',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `status` tinyint(1) DEFAULT '1' COMMENT '状态:0-禁用,1-启用',
  PRIMARY KEY (`id`),
  UNIQUE KEY `code` (`code`),
  KEY `idx_code` (`code`),
  KEY `idx_is_deleted` (`is_deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='权限表';
CREATE TABLE `role_permission` (
  `id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '映射ID,主键,使用字符串形式',
  `role_id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色ID,外键关联role表',
  `permission_id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '权限ID,外键关联permission表',
  `status` tinyint DEFAULT '1' COMMENT '状态:0-禁用,1-启用',
  `is_deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除标志:0-未删除,1-已删除',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_role_permission` (`role_id`,`permission_id`),
  KEY `idx_role_id` (`role_id`),
  KEY `idx_permission_id` (`permission_id`),
  KEY `idx_status` (`status`),
  KEY `idx_is_deleted` (`is_deleted`),
  CONSTRAINT `fk_role_permission_permission` FOREIGN KEY (`permission_id`) REFERENCES `permission` (`id`),
  CONSTRAINT `fk_role_permission_role` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色-权限映射表';
用户认证与权限控制整体流程概览
登录认证
  1. 用户提交用户名密码 → 后端验证 → 生成 JWT → 返回给前端。
  2. 前端后续请求携带 JWT(放在 Authorization: Bearer <token> 头中)。
  3. 后端通过 JWT 验证用户身份与权限,决定是否允许访问资源。
权限控制
  1. 请求到达 → JwtTokenFilter 拦截 → 提取并验证 JWT。
  2. 若验证通过 → 构建 Authentication 对象 → 存入 SecurityContext
  3. Spring Security 根据 Authentication 中的权限信息,判断是否有权访问资源。
框架主要类职责介绍
类名作用说明
JwtUtilJWT 工具类负责生成、解析、验证 JWT,提取用户名和权限等信息。
CustomUserDetails用户详情封装实现 UserDetails 接口,封装用户实体、角色、权限,供 Spring Security 使用。
CustomUserDetailsService用户详情服务实现 UserDetailsService 接口,根据用户名加载用户信息(包括角色和权限)。
JwtTokenFilterJWT 过滤器拦截请求,提取 JWT,验证并设置认证信息到 SecurityContext
JwtAuthenticationEntryPoint认证失败处理器当未认证用户访问受保护资源时,返回 401 Unauthorized。
SecurityConfig安全配置中心配置认证管理器、密码加密、过滤器链、放行规则等。
关键代码介绍
SecurityConfig类
protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()//关闭 CSRF 防护(跨站请求伪造)
                .exceptionHandling().authenticationEntryPoint(entryPoint).and()//当用户访问受保护资源但未认证或 token 无效时,由 entryPoint(即 JwtAuthenticationEntryPoint)处理,返回 401 Unauthorized
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()//设置会话策略为无状态,完全依赖 JWT 进行身份识别
                .authorizeRequests()
                .antMatchers("/auth/**").permitAll()
                .anyRequest().authenticated();// /auth/** 路径下的所有请求(如登录、注册)直接放行,不需要认证;其他所有路径必须认证(即必须携带有效 JWT)

        http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);//将自定义的 JwtTokenFilter 添加到过滤器链中
    }
UserDetailsService类

加载用户信息、角色、权限

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = userMapper.findByUsername(username);
    if (user == null) {
        throw new UsernameNotFoundException("用户不存在: " + username);
    }
    if (user.getStatus() != 1) {
        throw new UsernameNotFoundException("用户已被禁用: " + username);
    }
    // 获取用户角色
    List<String> roleIds = userRoleMapper.findRoleIdsByUserId(user.getId());
    List<Role> roles = roleMapper.listByIds(roleIds);
    // 获取角色对应的权限
    List<String> permissionIds = rolePermissionMapper.findPermissionIdsByRoleIds(roleIds);
    List<Permission> permissions = permissionMapper.listByIds(permissionIds);
    return new CustomUserDetails(user, roles, permissions);
}
UserDetails类

把当前用户的“角色”和“权限”统一转换成 Spring Security 能识别的“授权对象”集合(GrantedAuthority),供后续权限控制使用

public Collection<? extends GrantedAuthority> getAuthorities() {
    Set<GrantedAuthority> set = new HashSet<>();
    roles.forEach(r -> set.add(new SimpleGrantedAuthority(r.getRoleCode())));
    permissions.forEach(p -> set.add(new SimpleGrantedAuthority(p.getCode())));
    return set;
}
JwtUtil类

生成、解析、验证 token

public String generateToken(String username, List<String> authorities) {
    Date expiry = new Date(System.currentTimeMillis() + expiration);//计算 token 的过期时间
    return Jwts.builder()
            .setSubject(username)//将用户名作为 token 的“主体”,后续可根据这个字段识别用户身份
            .claim("authorities", authorities)//将用户的权限列表作为自定义声明(claim)存入 token
            .setIssuedAt(new Date())//设置 token 的签发时间,用于判断 token 是否过期或是否有效
            .setExpiration(expiry)//设置 token 的过期时间
            .signWith(SignatureAlgorithm.HS512, secret)//使用 HS512 算法和配置的密钥(secret)对 token 进行签名,防止篡改
            .compact();
}
JwtTokenFilter类

在每次请求到达时,拦截并解析 JWT,若合法则自动完成用户认证,把用户信息放入 Spring Security 上下文,使后续业务层/控制器能够拿到当前登录用户

protected void doFilterInternal(HttpServletRequest request,
                                HttpServletResponse response,
                                FilterChain chain) throws ServletException, IOException {
    String header = request.getHeader("Authorization");
    if (header != null && header.startsWith("Bearer ")) { //只处理 Bearer <token> 格式的 JWT;否则直接放过,留给后面的过滤器或 Spring Security 处理
        String token = header.substring(7);//去掉 "Bearer " 前缀,拿到纯 token 字符串
        if (jwtUtil.validate(token)) { //用 JwtUtil 验证签名、过期时间等;不合法就什么都不做,请求继续(后面会因为 SecurityContext 里没认证信息而被拦截)
            String username = jwtUtil.getUsername(token);
            UserDetails details = userDetailsService.loadUserByUsername(username);//根据用户名加载完整的用户信息(包含角色、权限等)
            UsernamePasswordAuthenticationToken auth =
                    new UsernamePasswordAuthenticationToken(details, null, details.getAuthorities());//构造 已认证 的 Authentication 对象
            SecurityContextHolder.getContext().setAuthentication(auth);//把认证信息写入 Spring Security 上下文(SecurityContext);当前线程的任何地方都可以通过 SecurityContextHolder.getContext().getAuthentication() 拿到用户信息
        }
    }
    chain.doFilter(request, response);//过滤器链继续向下执行,业务控制器最终拿到请求
}
权限控制方式
基于 URL 的权限控制(在 SecurityConfig 中配置)
.antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
基于注解的权限控制(在controller方法接口上加)

需要在SecurityConfig开启注解支持:@EnableGlobalMethodSecurity(prePostEnabled = true)

@PreAuthorize("hasRole('ADMIN')")//角色层面
public void someAdminOnlyMethod() { ... }

@PreAuthorize("hasAuthority('user:list')")//权限层面
public Result<PageInfo<User>> list(@RequestBody UserDto dto){ ... }

上述主要介绍了框架的重点内容,然后按自己的需求进行功能接口的设计编写就可以。

这是一个我个人编写的觉得比较完整的demo:提供注册,账密登录,电话登录,接口token校验,权限认证采用了接口级别,还有用户,角色,权限的增删查改等功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值