代码先锋网 代码片段及技术文章聚合

(二)、spring boot security 授权--自定义AccessDecisionManager和AccessDecisionVoter

技术标签: spring boot security  spring security  授权  AccessDecisionManager  AccessDecisionVoter

1、简介

spring security主要分为两部分,认证(authentication)和授权(authority)。

这一篇主要是授权部分,它由FilterSecurityInterceptor逻辑拦截处理,具体通过AccessDecisionManager实现。

1.1 系统授权实现说明

系统提供了三种实现方式:

  1. AffirmativeBased(spring security默认使用):
    只要有投通过(ACCESS_GRANTED)票,则直接判为通过。
    如果没有投通过票且反对(ACCESS_DENIED)票在1个及其以上的,则直接判为不通过。
  2. ConsensusBased(少数服从多数):
    通过的票数大于反对的票数则判为通过;通过的票数小于反对的票数则判为不通过;
    通过的票数和反对的票数相等,则可根据配置allowIfEqualGrantedDeniedDecisions(默认为true)进行判断是否通过。
  3. UnanimousBased(反对票优先):
    无论多少投票者投了多少通过(ACCESS_GRANTED)票,只要有反对票(ACCESS_DENIED),那都判为不通过;如果没有反对票且有投票者投了通过票,那么就判为通过.

这三种方式都包含了一个AccessDecisionManager(权限控制处理)和多个AccessDecisionVoter(投票项)。

1.2 自定义实现说明

系统默认提供的是基于ROLE(角色)的权限,这里自定义一下,处理 url + httpMethod 方式的权限拦截。

有三种方式可以实现:

方式一:
通过.access 方式实现。
步骤:
1. 自定义MyAuthService:实际权限校验服务
2. WebSecurityConfigurerAdapter配置:注入自定义校验服务

方式二:
通过.accessDecisionManager,覆盖AccessDecisionManager方式实现。
步骤:
1. 自定义AccessDecisionManager: 实现授权逻辑校验。
2. WebSecurityConfigurerAdapter配置:注入自定义AccessDecisionManager

方式三:
通过 添加AccessDecisionVoter投票项处理。
步骤:
1. 自定义AccessDecisionVoter: 实现授权投票逻辑
2. WebSecurityConfigurerAdapter配置:注入自定义AccessDecisionVoter

2、代码路径

github代码路径

3、方式一(.access 方式)步骤说明:

3.1、自定义MyAuthService

package demo.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component
public class MyAuthService {
    private Logger log = LoggerFactory.getLogger(this.getClass());

    /**
     *
     * @Description: 判断一个请求是否拥有权限。
     *
     * @auther: csp
     * @date:  2019/1/7 下午9:48
     * @param request
     * @param authentication
     * @return: boolean
     *
     */
    public boolean canAccess(HttpServletRequest request, Authentication authentication) {
        Object principal = authentication.getPrincipal();
        if(principal == null){
            return false;
        }

        if(authentication instanceof AnonymousAuthenticationToken){
            //check if this uri can be access by anonymous
            return false;
        }

        authentication.getAuthorities();
        String uri = request.getRequestURI();
        //check this uri can be access by this role

        // TODO 实际根据权限列表判断。
        log.info("=================== myAuth pass ===================");
        return true;

    }
}

3.2 WebSecurityConfigurerAdapter配置

 http
    .authorizeRequests()
    // .access 方式 校验是否有权限。
    .antMatchers("/user/**", "/").access("@myAuthService.canAccess(request,authentication)")
    .and().logout().logoutUrl("/logout").logoutSuccessUrl("/").and()
    .formLogin().loginPage("/login").defaultSuccessUrl("/").failureUrl("/login-error");

4、方式二(.accessDecisionManager)步骤说明:

4.1、自定义AccessDecisionManager

package demo.config;

import demo.model.UrlGrantedAuthority;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;

import java.util.Collection;

/**
 *
 * @Description: 自定义AccessDecisionManager,通过url和httpmethod拦截权限
 *
 * @auther: csp
 * @date:  2019/1/7 下午9:59
 *
 */
public class UrlMatchAccessDecisionManager implements AccessDecisionManager {


    @Override public boolean supports(ConfigAttribute attribute) {
            return true;
    }

    @Override public boolean supports(Class<?> clazz) {
        return true;
    }

    @Override public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {

        if (authentication == null) {
            throw new AccessDeniedException("无权限!");
        }

        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

        // 请求路径
        String url = getUrl(object);
        // http 方法
        String httpMethod = getMethod(object);

        boolean hasPerm = false;

        // request请求路径和httpMethod 和权限列表比对。
        for (GrantedAuthority authority : authorities) {
            if (!(authority instanceof UrlGrantedAuthority))
                continue;
            UrlGrantedAuthority urlGrantedAuthority = (UrlGrantedAuthority) authority;
            if (StringUtils.isEmpty(urlGrantedAuthority.getAuthority()))
                continue;
            //如果method为null,则默认为所有类型都支持
            String httpMethod2 = (!StringUtils.isEmpty(urlGrantedAuthority.getHttpMethod())) ?
                urlGrantedAuthority.getHttpMethod() :
                httpMethod;
            //AntPathRequestMatcher进行匹配,url支持ant风格(如:/user/**)
            AntPathRequestMatcher antPathRequestMatcher = new AntPathRequestMatcher(urlGrantedAuthority.getAuthority(),
                httpMethod2);
            if (antPathRequestMatcher.matches(((FilterInvocation) object).getRequest())) {
                hasPerm = true;
                break;
            }
        }

        if (!hasPerm) {
            throw new AccessDeniedException("无权限!");
        }
    }

    /**
     * 获取请求中的url
     */
    private String getUrl(Object o) {
        //获取当前访问url
        String url = ((FilterInvocation) o).getRequestUrl();
        int firstQuestionMarkIndex = url.indexOf("?");
        if (firstQuestionMarkIndex != -1) {
            return url.substring(0, firstQuestionMarkIndex);
        }
        return url;
    }

    private String getMethod(Object o) {
        return ((FilterInvocation) o).getRequest().getMethod();
    }
}

4.2、WebSecurityConfigurerAdapter配置

http
    .authorizeRequests()
      // 覆盖默认的AffirmativeBased授权逻辑。
    .accessDecisionManager(getAccessDecisionManager())
    .and().logout().logoutUrl("/logout").logoutSuccessUrl("/").and()
    .formLogin().loginPage("/login").defaultSuccessUrl("/").failureUrl("/login-error");

5、方式三(添加AccessDecisionVoter投票项)步骤说明:

5.1、自定义AccessDecisionVoter

package demo.service;

import demo.model.UrlGrantedAuthority;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;

import java.util.Collection;

/**
 *
 * @Description: 增加一个授权逻辑投票项,根据url和httpmethod判断权限。
 *
 * @auther: csp
 * @date:  2019/1/7 下午10:03
 *
 */
public class UrlMatchVoter implements AccessDecisionVoter<Object> {


    @Override public boolean supports(ConfigAttribute attribute) {
            return true;
    }

    @Override public boolean supports(Class<?> clazz) {
        return true;
    }

    @Override public int vote(Authentication authentication, Object object,
        Collection<ConfigAttribute> attributes) {

        if (authentication == null) {
            return ACCESS_DENIED;
        }


        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

        // 请求路径
        String url = getUrl(object);
        // http 方法
        String httpMethod = getMethod(object);

        boolean hasPerm = false;

        for (GrantedAuthority authority : authorities) {
            if (!(authority instanceof UrlGrantedAuthority))
                continue;
            UrlGrantedAuthority urlGrantedAuthority = (UrlGrantedAuthority) authority;
            if (StringUtils.isEmpty(urlGrantedAuthority.getAuthority()))
                continue;
            //如果method为null,则默认为所有类型都支持
            String httpMethod2 = (!StringUtils.isEmpty(urlGrantedAuthority.getHttpMethod())) ?
                urlGrantedAuthority.getHttpMethod() :
                httpMethod;
            //AntPathRequestMatcher进行匹配,url支持ant风格(如:/user/**)
            AntPathRequestMatcher antPathRequestMatcher = new AntPathRequestMatcher(urlGrantedAuthority.getAuthority(),
                httpMethod2);
            if (antPathRequestMatcher.matches(((FilterInvocation) object).getRequest())) {
                hasPerm = true;
                break;
            }
        }

        if (!hasPerm) {
            return ACCESS_DENIED;
        }

        return ACCESS_GRANTED;
    }

    /**
     * 获取请求中的url
     */
    private String getUrl(Object o) {
        //获取当前访问url
        String url = ((FilterInvocation) o).getRequestUrl();
        int firstQuestionMarkIndex = url.indexOf("?");
        if (firstQuestionMarkIndex != -1) {
            return url.substring(0, firstQuestionMarkIndex);
        }
        return url;
    }

    private String getMethod(Object o) {
        return ((FilterInvocation) o).getRequest().getMethod();
    }
}

5.2、WebSecurityConfigurerAdapter配置

http
			// 将tokenfilter追加进去,筛选出来tokenLogin逻辑。
			.addFilterBefore(getTokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
			.logout().logoutUrl("/logout").logoutSuccessUrl("/").and()
			.formLogin().loginPage("/login").defaultSuccessUrl("/").failureUrl("/login-error").permitAll().and()
			.authorizeRequests()
			.antMatchers(MyTokenAuthenticationFilter.SPRING_SECURITY_RESTFUL_LOGIN_URL).permitAll()
			.antMatchers("/admin/**").hasRole("ADMIN")
			.antMatchers("/user/**").hasRole("USER")
			.anyRequest().authenticated()
			// 修改授权相关逻辑
			.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
				public <O extends FilterSecurityInterceptor> O postProcess(
					O fsi) {

//					// 覆盖SecurityMetadataSource
//					fsi.setSecurityMetadataSource(fsi.getSecurityMetadataSource());

//					// 覆盖AccessDecisionManager
//					fsi.setAccessDecisionManager(getAccessDecisionManager());

					// 为默认的AffirmativeBased逻辑增加投票项,
					AccessDecisionManager accessDecisionManaer = fsi.getAccessDecisionManager();
					if (accessDecisionManager instanceof AbstractAccessDecisionManager) {
						((AbstractAccessDecisionManager) accessDecisionManager).getDecisionVoters().add(new UrlMatchVoter());
					}

					return fsi;
				}
			});

5.3 验证

user 通过角色授权(ROLE),
admin 通过自定义投票项 UrlMatchVoter 授权。
由于AuthenticationManager使用的是默认的AffirmativeBased,所以只要有一个通过,则说明有权限。

版权声明:本文为qq_30062125原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_30062125/article/details/86031713

智能推荐

spring boot security 实战

最近需要新做一个项目,后端是spring boot ,由于涉及到用户权限验证,于是去看了看spring security,在这里记录一下配置过程 首先引入依赖 我这里应该是比较新的版本,貌似我查了下是5以上的,顺便贴出spring boot版本 这时候启动项目,访问任意路径,你会发现页面转到/login页面 这是security内置的一个页面,如果不做任何配置时,未登录的请求将会被拦截到...

Spring boot security

pom依赖 加入依赖后启动项目就已经对资源实现保护,用户名默认user,密码在项目启动中默认打印在控制台中. 当然也可以在application.yml中配置 以上作为学习简单了解下,实际这么配怕是要被祭天… 下面将从数据库中提取用户来实现 第一步:WebSecurityConfigurerAdapter 第二步:UserDetailsService 第三步:UserDetails ...

Security整合spring boot

1、基础概念 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring的IOC,DI,AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为安全控制编写大量重复代码的工作。 2、核心API解读 1、SecurityContextHolde...

Spring Boot Security 整合

随笔记录 demo目前在Spring Boot 中整合了Security,Mybatis,SQLite等组件 1.Maven中的POM文件 2.入口类增加Mybatis相关扫描注解 3.appliaaction.yml文件整体配置 4.创建authentication包,进行security的相关配置类 5.自定义管理类 到这里Security的相关配置阶段完成。...

spring boot security mybitis

.pom文件需要引入 配置中的URL不要入库,csrf token 默认是true spring boot 的spring security配置代替spring mvc的spring security xxx.xml配置 拦截器实现,根据需求实现。若之前的数据表和spring security不兼容,可以更具数据表的情况,对所有requestUrl放行,拦截特定的url。这里去掉实现逻辑。 加载已...

猜你喜欢

spring boot security学习

spring boot security(一) 配置认证和授权 通过继承WebSecurityConfigurerAdapter,可以重写其认证和授权的逻辑。 实现UserDetailsService接口 注入bean BCryptPasswordEncoder Encoded password does not look like BCrypt 数据库传递的密码没有经过BCrypt加密。 解决方...

Spring boot整合Security

Spring boot整合Security 概述 Spring Security 是spring项目之中的一个安全模块 WebSecurityConfigurerAdapter: 自定义Security策略 AuthenticationManagerBuilder: 自定义认证策略 @EnableWebSecurity: 开启WebSecurity模式 Spring Security的两个主要目标...

spring security自定义认证和自定义授权

spring security自定义认证和自定义授权 第一种方式:自定义login接口: 通过配置对自定义的登录放行,实现登录方式; 认证是否登录的对象是继承BasicAuthenticationFilter类,(认证方式我使用token认证,在登录接口里面把信息缓存到redis),通过BasicAuthenticationFilter的子类去做认证,代码就不贴了,大概的逻辑是这样的:判断指定的请...

Spring Security 与Spring Boot结合的认证授权机制流程(二)

接着上一篇博客继续说明Spring Security 与Spring Boot结合后的使用 框架原理流程: 1 容器启动,spring security 从库里头加载权限信息 并以map的形式存取 (HashMap resourceMap key:资源url,value:角色名称 ROLE_为前缀的值) 2 用户进行登录操作: 加载用户的角色列表(Collection ) loadUserByUs...

Spring Boot 整合Spring Security + JWT 实现权限认证授权

Pom 依赖: 从源码中(源码教程),或者Spring Security 官方文档我们可以知道Spring Security将账号、密码等信息是封装到UsernamePasswordAuthenticationToken 对象中去,那么我们可以继承这个类来自定义我们自己的东西。 从源码中我们可以知道,AuthenticationManager 是一个总览全局的对象,我们登陆的时候就是将封装好的Jw...