Spring Security(十六):授权(Authorization)

一:简介

授权

就是控制url能不能访问。一个请求过来会先经过FilterSecurityInterceptor过滤器拦截,然后调用访问决定管理器AccessDecisionManager,访问决定管理器是通过访问决定投票器AccessDecisionVoter来投票的,如果投票器投通过那么这个url就可以访问,如果投票器投拒绝那么这个url就不能被访问,会抛出一个访问被拒绝的异常。

访问决定投票器AccessDecisionVoter

AccessDecisionVoter接口的实现类是WebExpressionVoter,该投票器通过比较SecurityConfiguration配置的表达式字符串与用户所拥有的权限集合做比较,比较结果如果为true表示投票通过可以访问,如果为false表示投票拒绝不允许访问。

权限表达式

就是跟在http.authorizeRequests().antMatchers("/xxx")后面的方法就是权限表达式,Spring Security会将此权限表达式和UserDetailsService#loadUserByUsername方法回去的User中的authorities权限集合做包含判断,如果包含则返回true,不包含返回false
在这里插入图片描述
在这里插入图片描述

  • permitAll() 不需要登录,所有人都可以访问,永远返回true
  • denyAll() 永远返回false,任何情况下都不能访问
  • anonymous() 当前用户是anonymous时返回true, 即当前用户没有登录,属于匿名用户
  • authenticated() 当前用户不是anonymous时返回true,可以是用户登录的或者使用记住我的都是认证的rememberMe
  • fullyAuthenticated() 当前用户既不是anonymous也不是rememberMe时返回true,即是通过登录来认证的
  • hasRole(String role) 必须有指定权限才可以访问, Spring Security 会自动对参数值增加一个前缀ROLE_,即如果参数传admin,那么最终会变成"ROLE_admin"
  • hasAnyRole(String… roles) 拥有任意一个角色权限就可以访问
  • hasAuthority(String authority) 拥有指定的权限时返回true,对于判断Authority的Spring Security会直接判断,不会对参数拼接前缀
  • hasAnyAuthority(String… authorities) 拥有任意一个权限就可以访问
  • hasIpAddress(String ipaddressExpression) 请求发来的ip地址匹配时返回true
  • access(String attribute) attribute可以是其它表达式,也可以通过and 将其它多个表达式连接在一起,如access("hasRole('ROLE_USER') and hasRole('ROLE_SUPER')"),权限表达式也可以自定义,然后通过access来配置

注意:hasRole和hasAnyRole都会自动对参数拼接前缀"ROLE_", hasAuthority和hasAnyAuthority不会拼接任何前缀。
自己感觉Spring Security中的角色Role和权限Authority的定义并不像我们认为的一个角色对应于多个权限那样的概念,这里的权限好像加上"ROLE_"前缀就变成角色了。

UserDetailsService#loadUserByUsername方法返回的User对象中User(String username, String password, Collection<? extends GrantedAuthority> authorities) 封装的也是权限。

二:代码实现

public interface RbacService {
    /** 判断用户有没有权限访问该请求 */
    boolean hasPermission(HttpServletRequest request, Authentication authentication);
}
@Component("rbacServiceImpl")
public class RbacServiceImpl implements RbacService {

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        boolean hasPermission = false;

        Object principal = authentication.getPrincipal();
        if (principal instanceof UserDetails) {
            String username = ((UserDetails) principal).getUsername();
            // TODO 根据用户名去加载数据库中的用户对应的权限
            Set<String> urls = new HashSet<>();
            urls.add("/getUserList");

            for (String url : urls) {
                if (antPathMatcher.match(url, request.getRequestURI())) {
                    hasPermission = true;
                    break;
                }
            }
        }

        return hasPermission;
    }
}
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		 // 其它任意请求都会调用RbacServiceImpl#hasPermission方法来判断用户有没有权限访问
	    http.authorizeRequests()
	    	.antMatchers("/login", "/logout", "/regist").permitAll()
	    	.anyRequest().access("@rbacServiceImpl.hasPermission(request, authentication)");
	}
}
  1. 访问http://localhost:8080/login 进行登录
  2. 登录成功后访问http://localhost:8080/getUserList可以访问
  3. 访问http://localhost:8080/getOrderList不可以访问
    在这里插入图片描述

在这里插入图片描述

发布了308 篇原创文章 · 获赞 936 · 访问量 133万+

猜你喜欢

转载自blog.csdn.net/vbirdbest/article/details/95166290