spring security 授权方式(自定义)及源码跟踪

技术spring security 授权方式(自定义)及源码跟踪 spring security 授权方式(自定义)及源码跟踪spring security 授权方式(自定义)及源码跟踪
? 这节我们

安全授权模式(自定义)和来源跟踪

spring security 授权方式(自定义)及源码跟踪

?在这一节中,让我们来看看spring security的几种授权方法和简短的源代码跟踪。刚接触spring security的时候,为了实现它的授权,尤其是它的自定义授权,在网上找了很多文章和例子,让我觉得很难。但是现在我试着自己从官方文件和演示中学习,这是相当有益的。

基于表达式Spel的访问控制

?保安用Spring EL做表情支持,不知道Spring EL的童鞋自己学。根据文档https://docs . spring . io/spring-security/site/docs/5 . 4 . 9/reference/html 5/# El-access,检查IDEA中SecurityExpressionRoot类的上下继承关系。SecurityExpressionOperations声明每个表达式接口,最后WebSecurityExpressionRoot和MethodSecurityExpressionRoot实现每个特定的表达式逻辑。继承关系如下:

?在这里,我们可以知道最常用的方式是基于Web\Method对我们的应用进行授权。让我们看一下SecurityExpressionRoot类中定义的最基本的SpEL:

?我们简单介绍几个表达式接口:

Expression

描述

哈希角色(字符串角色)

如果当前主体具有指定的角色,则返回true。

有角色(字符串…角色)

如果当前主体具有任何提供的角色,则返回true。

权限(字符串权限)

如果当前主体具有指定的权限,则返回true。

哈桑亚权限(字符串…权限)

如果当前主体具有任何提供的权限,则返回true。

证明

允许直接访问从SecurityContext获取的当前身份验证对象。

主要的

允许直接访问代表当前用户的主体对象。

授权方式

基于Web/Url的安全表达式

此方法可用于单个或一批Url的安全验证,例如

@覆盖

受保护的无效配置(HttpSecurity http)引发异常{

超文本传送协议(Hyper Text Transport Protocol的缩写)。authorizeRequests()

//对于接口/admin/api/hello,需要p1权限。antMatchers('/admin/api/hello ')。有权限(' p1 ')

//访问/user/api/**的接口需要用户权限。antMatchers('/user/api/** ')。hasRole('USER ')。antMatchers('/app/api/** ')。permitAll()。antMatchers('/css/** ','/index ')。permitAll()。antMatchers('/user/** ')。hasRole('USER ')。和()。表单登录()。登录页面('/login ')。failure URl(“/log in-error”)。permitAll();

}

基于Method的安全表达式

方法安全表达式

方法安全性比简单的允许或拒绝规则稍微复杂一点。春季安全3.0引入了一些新的

nnotations in order to allow comprehensive support for the use of expressions.

Spring Security 3.0 引入了一些新的注解,以便全面支持表达式的使用,分别是@PreAuthorize, @PreFilter, @PostAuthorize and @PostFilter相信大家有web开发的基础,不难知道这几个注解的意思。

  • @PreAuthorize:在访问方法前进行鉴权

  • @PreFilter:同上

  • @PostAuthorize:在访问方法后进行鉴权

  • @PostFiltert:同上

    public class AdminController {
        @GetMapping("hello")
        @PostAuthorize("hasRole('User')")
        public String hello() {
            return "hello, admin";
        }
        @GetMapping("p1")
        @PreAuthorize("hasAuthority('p1')")
        public String p1() {
            return "hello, p1";
        }
    }
    

但是基于方法的需要事先在配置类添加注解,表示开启方法验证。

@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)

根据官网介绍还有其他2中方式(基于AOP 、spring security原生的注释@Secure),有兴趣的小伙伴可以自行参阅https://docs.spring.io/spring-security/site/docs/5.4.9/reference/html5/#secure-object-impls

授权原理

? 根据文档https://docs.spring.io/spring-security/site/docs/5.4.9/reference/html5/#secure-object-impls指出,spring security提供了拦截器来控制安全对象的访问,例如方法调用、web请求。AccessDecisionManager 做出关于是否允许调用继续进行的调用前决定。

? 查看AccessDecisionManager 接口:

decide(Authentication authentication, Object object, CollectionConfigAttribute configAttributes)

参数说明:

  • authentication:当前登录对象主体
  • object: 当前安全保护对象
  • configAttributes:访问当前对象必须要有的权限属性

再看看看它的三个实现类,默认的实现是根据各个实现类的投票机制来决定是否能够访问当前安全保护对象的:

AffirmativeBased:只要configAttributes中有一个权限满足就可以访问当前保护对象

ConsensusBased:满足超过一半的权限就能够访问当前保护对象

UnanimousBased:configAttributes中所有的权限都满足才能访问当前保护对象

由于我们不知道默认是哪个实现类,所以我们在三个类上的decide方法都打上断点,这样我们就能知道默认是哪个实现类了,

内部的投票实现有兴趣的小伙伴自行探索,到这样我们大概就明白spring security默认的授权实现机制了。接着我们根据该机制去实现我们的自定义授权方式。

给出官网的一张原理图

  1. 首先,FilterSecurityInterceptor 从 SecurityContextHolder 获得一个 Authentication
  2. 其次,FilterSecurityInterceptor 从传入 FilterSecurityInterceptor 的 HttpServletRequest、HttpServletResponse 和 FilterChain 创建一个 FilterInvocation
  3. 接下来,它将 FilterInvocation 传递给 SecurityMetadataSource 以获取 ConfigAttributes
  4. 最后,它将 Authentication、FilterInvocation 和 ConfigAttributes 传递给 AccessDecisionManager。
    1. 如果授权被拒绝,则抛出 AccessDeniedException。在这种情况下,ExceptionTranslationFilter 处理 AccessDeniedException
    2. 如果访问被授予,FilterSecurityInterceptor 继续使用允许应用程序正常处理的 FilterChain。

自定义授权方式

? 根据葫芦画瓢,我们首先需要

1、自定义一个AccessDecisionManager实现类,让它确定到底是否能够鉴权通过,能够访问保护对象;

@Component
public class CustomUrlDecisionManager implements AccessDecisionManager {
    @Override
    public void decide(Authentication authentication, Object object, CollectionConfigAttribute configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        for (ConfigAttribute configAttribute : configAttributes) {
            String needRole = configAttribute.getAttribute();
            if ("ROLE_LOGIN".equals(needRole)) {
                if (authentication instanceof AnonymousAuthenticationToken) {
                    throw new AccessDeniedException("尚未登录,请登录!");
                }else {
                    return;
                }
            }
            Collection extends GrantedAuthority authorities = authentication.getAuthorities();
            for (GrantedAuthority authority : authorities) {
                if (authority.getAuthority().equals(needRole)) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("权限不足,请联系管理员!");
    }
    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }
    @Override
    public boolean supports(Class clazz) {
        return true;
    }
}

2、接着实现一个FilterInvocationSecurityMetadataSource实现类,这个类给出访问保护对象具体需要的哪些权限。

/**
 * 这个类的作用,主要是根据用户传来的请求地址,分析出请求需要的角色
 */
@Component
public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
    @Autowired
    MenuService menuService;
    AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Override
    public CollectionConfigAttribute getAttributes(Object object) throws IllegalArgumentException {
        String requestUrl = ((FilterInvocation) object).getRequestUrl();
        ListMenu menus = menuService.getAllMenusWithRole();
        for (Menu menu : menus) {
            if (antPathMatcher.match(menu.getUrl(), requestUrl)) {
                ListRole roles = menu.getRoles();
                String[] str = new String[roles.size()];
                for (int i = 0; i  roles.size(); i++) {
                    str[i] = roles.get(i).getName();
                }
                return SecurityConfig.createList(str);
            }
        }
        return SecurityConfig.createList("ROLE_LOGIN");
    }
    @Override
    public CollectionConfigAttribute getAllConfigAttributes() {
        return null;
    }
    @Override
    public boolean supports(Class clazz) {
        return true;
    }
}

3、将上面2个对象添加到拦截器中,给FilterSecurityInterceptor重新设置它的这2个属性

http.authorizeRequests()
        .withObjectPostProcessor(new ObjectPostProcessorFilterSecurityInterceptor() {
            @Override
            public O extends FilterSecurityInterceptor O postProcess(O object) {
                object.setAccessDecisionManager(customUrlDecisionManager);
                object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);
                return object;
            }
        })

? 相信到这里,小伙伴也能根据自己实际项目需要怎样的授权方式去进行实现了,如果是AOP/@secure方式的则需要再看一下文档说明。好了,spring security的章节就到这里,后面继续学习spring security oauth2的章节。

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/132067.html

(1)

相关推荐

  • 图联通性问题,Tarjan)整理

    技术图联通性问题,Tarjan)整理 图联通性问题(Tarjan)整理笑死,根本学不会笑死,根本学不会。(烂尾力
    Tarjan算法是用于处理图连通性相关的一类算法。
    1、强连通分量、双连通分量、割点与桥

    礼包 2021年11月20日
  • oracle提供哪几种分区表的特点(oracle分区表分区备份有哪些方法)

    技术ORACLE分区表日常维护方法是什么这篇文章主要讲解了“ORACLE分区表日常维护方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“ORACLE分区表日常维护方

    攻略 2021年12月22日
  • 死的说法有哪些,“死”的种种说法有哪些

    技术死的说法有哪些,“死”的种种说法有哪些“死”的叫法因时而异,也因死者 的身世、死的原因和方式不同而迥然各异死的说法有哪些。表示等级观念的:皇帝死叫“崩”, 诸侯死叫“薨”,一般士大夫的死,叫“卒”。死的原因和方式不同

    生活 2021年10月26日
  • C++三只小猪谁最重

    技术C++三只小猪谁最重 C++三只小猪谁最重C++选择结构学习小案例#includeiostream
    using namespace std;int main()
    {int A = 0;cout "

    礼包 2021年12月2日
  • 喜怎么读,喜字下面加个心字念什么

    技术喜怎么读,喜字下面加个心字念什么憙 拼音:xǐ 部首:心,部外笔画:12,总笔画:16五笔86&98:FKUN 仓颉:GRP 笔顺编号:1212514312514544 四角号码:40336 UniCode:

    生活 2021年10月27日
  • 七种基本UML元件分别是什么

    技术七种基本UML元件分别是什么这篇文章给大家介绍七种基本UML元件分别是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。UML元件上节的讲解是按照功能对UML图形进行的分类,绘制UML图形时,我

    攻略 2021年11月23日