Definition of Role and Authority

Role(角色):Role是一组权限(Authorities)的集合。

基于角色的访问控制(Role-Based Access Control, RBAC),用户被赋予不同的角色,角色与权限相关联。

Authority(权限):标识用户具有的具体权限。每个权限代表一个被授予的操作或资源访问的权限,权限包括一个接口、资源访问、系统的一个功能点。

Spring Security使用spring-boot-starter-security依赖时,默认基于权限而不是角色。

用户可以自定义实现基于角色的访问控制。

自定义角色

public class Authority {
    private Long id;
    private String name;
}
public class Role {
    private Long id;
    private String name;
    private List<Authority> authorities;
}
public class UserInfo {
    private Long id;
    private List<Role> roles;
}
CREATE TABLE role_authority (
    role_id BIGINT,
    authority_id BIGINT,
    PRIMARY KEY (role_id, authority_id)
);
CREATE TABLE user_role (
    user_id BIGINT,
    role_id BIGINT,
    PRIMARY KEY (user_id, role_id)
);
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;

@Component("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws 			UsernameNotFoundException {
        UserInfo user = userRepository.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("User not found");
        }
        return createSpringSecurityUser(user);
    }
    
    public User createSpringSecurityUser(UserInfo user) {
        Set<GrantedAuthority> authorities = new ArrayList<>();
        List<Role> roles = user.getRoles();
        for(Role role:roles) {
            for(Authority authority:role.getAuthorities()) {
                authorities.add(new SimpleGrantedAuthority(authority.getName()));
            }
        }
        authorities = authorities.stream().distinct().collect(Collectors.toList());
        return new User(user.getUsername(), user.getPassword(), authorities);
    }
}

注:上述代码中用户有可能有多个角色,不同角色可能有相同的权限,最后一步需要权限去重。

实例

TestController

@Slf4j
@RestController
@RequestMapping("/api/v1/version")
public class VersionController {
    @GetMapping("/test1")
    public String test1() {
        return "test1";
    }

    @GetMapping("/test2")
    public String test2() {
        return "test2";
    }
    @GetMapping("/test3")
    public String test3() {
        return "test3";
    }

    @GetMapping("/test4")
    public String test4() {
        return "test4";
    }
}

WebSecuirtyConfig

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
		httpSecurity
				.cors()
				.and()
				// we don't need CSRF because our token is invulnerable
				.csrf().disable()
				// set exception handler
				.exceptionHandling()
				.authenticationEntryPoint(authenticationErrorHandler)
				.accessDeniedHandler(jwtAccessDeniedHandler)
				// enable h2-console
				.and()
				.headers().frameOptions().disable().and()
				.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
				// set URL for accessing free
				.and()
				.authorizeRequests()
				.mvcMatchers(HttpMethod.GET, "/api/v1/version/test1").hasAnyAuthority("ROLE_DOC_GET", "ROLE_DOC_DELETE")
				.mvcMatchers(HttpMethod.GET, "/api/v1/version/test2").hasAuthority("DOC_GET")
				.mvcMatchers(HttpMethod.GET, "/api/v1/version/test3").hasRole("DOC_GET")
				.mvcMatchers(HttpMethod.GET, "/api/v1/version/test4").hasAnyRole("DOC_GET", "DOC_DELETE")
				.anyRequest().authenticated()
				.and()
				.apply(securityConfigurerAdapter());
}

用户角色-权限

Spring Security - hasRole and hasAuthority_java

运行结果

Spring Security - hasRole and hasAuthority_User_02

Spring Security - hasRole and hasAuthority_spring_03

 

Spring Security - hasRole and hasAuthority_spring_04

Spring Security - hasRole and hasAuthority_spring boot_05

 结论

  • Authority = ROLE_ + Role

需要使用Authority作为Role时,DB设置时需要以ROLE_为前缀。

  • 多角色的本质还是多权限,Spring Security默认基于权限处理。实际用户有多个角色时,判断使用hasAuthority/hasAnyAuthority更方便