Spring Boot 实现权限管理(上)

Spring Boot 实现权限管理

Spring Boot 实现权限管理大致有2种方式,一种比较常规且简单的Spring Boot + Spring Security + MyBatis,另一种Spring Boot + Spring Security + JWT。

我们先要明白权限管理的本质,就是一系列挡在控制器前面的过滤器,实现权限管理,也就是设置并实现过滤器的过程。

方案1:Spring Boot + Spring Security + MyBatis

(1)你的数据库要先有sys_user、sys_role和sys_user_role这些表来表示用户和权限的链接。

(2)实现 UserDetailsService:

UserDetailsService 是 Spring Security 框架里的一个核心接口。其作用是从存储(如数据库、LDAP 等)中加载用户信息,为认证和授权流程提供必要的用户数据。

java 复制代码
@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 1. 查询用户(以手机号登录为例)
        SysUser user = userMapper.findByPhoneNumber(phoneNumber)
                .orElseThrow(() -> new UsernameNotFoundException("用户不存在"));

        // 2. 查询用户的角色和权限
        List<GrantedAuthority> authorities = new ArrayList<>();
        //GrantedAuthority 是 Spring Security 框架里的一个接口,它代表了用户所拥有的权限或者角色。在 Spring Security 的认证和授权体系中,GrantedAuthority 扮演着关键角色,主要用于描述用户被授予的权限信息。
        
        // 2 添加角色
        user.getRoles().forEach(role -> {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        });

        // 3. 返回 Spring Security 的 User 对象
        return new org.springframework.security.core.userdetails.User(
                user.getUsername(),
                user.getPassword(),
                authorities
        );
    }
}

UserDetails 是 Spring Security 框架里的一个核心接口,其主要作用是封装用户的核心信息,这些信息会在认证和授权过程中被使用。Spring Security 在进行用户认证和权限检查时,需要依据 UserDetails 对象里的信息来判断用户是否具备相应的权限。

java 复制代码
package org.springframework.security.core.userdetails;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;

import java.io.Serializable;
import java.util.Collection;

public interface UserDetails extends Serializable {
    // 获取用户的权限集合
    Collection<? extends GrantedAuthority> getAuthorities();
    // 获取用户的密码
    String getPassword();
    // 获取用户名
    String getUsername();
    // 判断用户账户是否未过期
    boolean isAccountNonExpired();
    // 判断用户账户是否未被锁定
    boolean isAccountNonLocked();
    // 判断用户的凭证(如密码)是否未过期
    boolean isCredentialsNonExpired();
    // 判断用户账户是否启用
    boolean isEnabled();
}

(3)配置 Spring Security

在用户登录以后刚刚写的CustomUserDetailsService 是隐式调用的,完全由Spring Boot管理和调用,后续只需要配置SecurityConfig,SecurityConfig便会根据CustomUserDetailsService返回的结果进行拦截,即权限控制。

java 复制代码
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/login", "/public/​**​").permitAll()  // 公开接口
                .requestMatchers("/user/​**​").hasAuthority("user") // 需具体权限
                .anyRequest().authenticated()                         // 其他接口需登录
            )

        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();  // 密码加密
    }
}

现在就可以在整个Spring Boot项目中实现权限管理了。

权限控制方式

scss 复制代码
permitAll():无条件允许任何形式访问,不管你登录还是没有登录。
anonymous():允许匿名访问,也就是没有登录才可以访问。
denyAll():无条件决绝任何形式的访问。
authenticated():只允许已认证的用户访问。
fullyAuthenticated():只允许已经登录或者通过 remember-me 登录的用户访问。
hasRole(String) : 只允许指定的角色访问。
hasAnyRole(String) : 指定一个或者多个角色,满足其一的用户即可访问。
hasAuthority(String):只允许具有指定权限的用户访问hasAnyAuthority(String):指定一个或者多个权限,满足其一的用户即可访问。
hasIpAddress(String) : 只允许指定 ip 的用户访问。

(1)URL 级别权限​:

在 SecurityConfig 中配置:

java 复制代码
.requestMatchers("/admin/​**​").hasRole("ADMIN")
.requestMatchers("/user/​**​").hasAuthority("user")

(2)方法级别权限​​

使用 @PreAuthorize 注解:

java 复制代码
@RestController
@RequestMapping("/api/user")
public class UserController {

    // 需 ADMIN 角色或 user 权限
    @PreAuthorize("hasRole('ADMIN') or hasAuthority('user')")
    @DeleteMapping("/{id}")
    public String deleteUser(@PathVariable Long id) {
        return "删除用户成功";
    }

    // 需 user 权限
    @PreAuthorize("hasAuthority('user')")
    @GetMapping("/{id}")
    public String getUser(@PathVariable Long id) {
        return "用户信息";
    }
}

注意 ​​:需在配置类上添加@EnableGlobalMethodSecurity

java 复制代码
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig {
}

这种方法对性能不友好,因为每一次请求都会触发过滤器去调动mapper层方法,对数据库进行访问,一旦请求量大了,数据库的压力也大。

另一种方案在下篇介绍。

相关推荐
码事漫谈5 分钟前
智能体颠覆教育行业调研报告:英语、编程、语文、数学学科应用分析
后端
蓝-萧9 分钟前
使用Docker构建Node.js应用的详细指南
java·后端
多喝开水少熬夜18 分钟前
Trie树相关算法题java实现
java·开发语言·算法
码事漫谈37 分钟前
《C语言点滴》——笑着入门,扎实成长
后端
Tony Bai1 小时前
【Go模块构建与依赖管理】09 企业级实践:私有仓库与私有 Proxy
开发语言·后端·golang
lkbhua莱克瓦241 小时前
Java基础——集合进阶用到的数据结构知识点1
java·数据结构·笔记·github
咖啡教室2 小时前
每日一个计算机小知识:ICMP
后端·网络协议
间彧2 小时前
OpenStack在混合云架构中通常扮演什么角色?
后端
咖啡教室2 小时前
每日一个计算机小知识:IGMP
后端·网络协议
间彧2 小时前
云原生技术栈中的核心组件(如Kubernetes、Docker)具体是如何协同工作的?
后端