Spring Security实现基于角色的权限控制

发布于:2025-06-15 ⋅ 阅读:(14) ⋅ 点赞:(0)

Spring Security 的权限控制是通过 SecurityContextHolder 中保存的认证信息(Authentication)进行的。权限信息来自 UserDetails 的 getAuthorities() 方法。

只要保证返回的 Collection<? extends GrantedAuthority> 包含 ROLE_xxx,就可以进行角色控制。

数据库设计

CREATE TABLE t_user (
  id INT PRIMARY KEY AUTO_INCREMENT,
  login_act VARCHAR(50),
  login_pwd VARCHAR(100),
  name VARCHAR(50),
  ...
);

CREATE TABLE t_role (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(50),        -- 角色名,例如 ADMIN、USER
  role_key VARCHAR(50)     -- 用于权限控制,例如 ROLE_ADMIN、ROLE_USER
);

CREATE TABLE t_user_role (
  user_id INT,
  role_id INT,
  PRIMARY KEY (user_id, role_id)
);

对应实体类如下:

@Data
@TableName("t_user")
public class TUser {
    private Integer id;
    private String loginAct;
    private String loginPwd;
    private String name;
    // 不加 roles 字段到数据库映射,但需要接收它
    @TableField(exist = false)
    private List<TRole> roles;
}


@Data
@TableName("t_role")
public class TRole {
    private Integer id;
    private String name;
    private String roleKey; // 例如 ROLE_ADMIN
}

Mapper

@Mapper
public interface TUserMapper extends BaseMapper<TUser> {
    TUser findByLoginAct(String loginAct);
}


@Mapper
public interface TRoleMapper extends BaseMapper<TRole> {
    @Select("SELECT r.* FROM t_role r JOIN t_user_role ur ON r.id = ur.role_id WHERE ur.user_id = #{userId}")
    List<TRole> selectByUserId(@Param("userId") Integer userId);
}

LoginUser 实现 UserDetails

ok 封装这里在我上一篇博客说了,你想获得登录用户的信息就得去实现UserDetails接口,所以不多赘述了

public class LoginUser implements UserDetails {

    private final TUser user;

    public LoginUser(TUser user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return user.getRoles().stream()
            .map(role -> new SimpleGrantedAuthority(role.getRoleKey()))
            .collect(Collectors.toList());
    }

    @Override public String getPassword() { return user.getLoginPwd(); }
    @Override public String getUsername() { return user.getLoginAct(); }
    @Override public boolean isAccountNonExpired() { return user.getAccountNoExpired() == 1; }
    @Override public boolean isAccountNonLocked() { return user.getAccountNoLocked() == 1; }
    @Override public boolean isCredentialsNonExpired() { return user.getCredentialsNoExpired() == 1; }
    @Override public boolean isEnabled() { return user.getAccountEnabled() == 1; }

    public TUser getUser() { return user; }
}

实现 UserDetailsService

依旧在我上一篇博客说了,Spring Security就是通过UserDetailService的loadUserByUsername方法来获取用户登录信息的,只不过这里我们相比于上一篇博客额外set了一下roles这个属性

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired private TUserMapper userMapper;
    @Autowired private TRoleMapper roleMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        TUser user = userMapper.findByLoginAct(username);
        if (user == null) throw new UsernameNotFoundException("用户不存在");

        List<TRole> roles = roleMapper.selectByUserId(user.getId());
        user.setRoles(roles);
        return new LoginUser(user);
    }
}

controller

你数据库中 role_key 是 ROLE_ADMIN,Spring Security 会自动把 hasRole(“ADMIN”) 转为 hasAuthority(“ROLE_ADMIN”) 匹配权限。所以你只需要:

数据库中角色字段保存如 ROLE_ADMIN

LoginUser 中返回 SimpleGrantedAuthority(“ROLE_ADMIN”)

就可以和controller层的注释 @PreAuthorize(“hasRole(‘ADMIN’)”)匹配上了

@RestController
public class UserController {

    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/admin/dashboard")
    public String admin() {
        return "管理员页面";
    }

    @GetMapping("/me")
    public String me(@AuthenticationPrincipal LoginUser user) {
        return "当前用户:" + user.getUser().getName();
    }
}

安全配置类别SecurityConfig

别忘了配置这个类

@Configuration
@EnableMethodSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
            .csrf(AbstractHttpConfigurer::disable)
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .formLogin(Customizer.withDefaults())
            .build();
    }

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