• Shiro核心概述



    0、写在前面的话

    最近在考虑权限相关的东西,于是就找到了Shiro,开涛老师的Shiro教程博客(《跟我学Shiro》)写得实在很好还带所有源码,所以我也就没有自己再总结各个阶段的笔记,只在这里对整个框架的核心类和部分执行过程进行了梳理和概述,以作备忘。


    1、Shiro的主要特性


    Shiro提供了如上图所示的特性,其中主要特性(其开发团队称之为应用安全的四大基石)如下:
    • Authentication - 身份认证 (与登陆相关,确定用户是谁)
    • Authorization - 确认权限 (确定用户能访问什么)
    • Session Management - 会话管理 
    • Cryptography - 数据加密


    2、Shiro如何工作

    2.1 从外部看Shiro


    应用代码的交互对象是 “Subject”,该对象代表了当前 “用户”,而所有用户的安全操作都会交给 SecurityManager 来管理,而管理过程中会从 Realm 中获取用户对应的角色和权限,可以把 Realm 堪称是安全数据源。

    也就是说,我们要使用最简单的 Shiro 应用:
    • 通过 Subject 来进行认证和授权,而 Subject 又委托给了 SecurityManager 进行管理
    • 我们需要给 SecurityManager 注入 Realm 以便其获取用户和权限进行判断
    • (也即,Shiro 不提供用户和权限的维护,需要由开发者自行通过 Realm 注入)

    2.2 从内部看Shiro

    如上所述,也就可以明白 Shiro 内部的架构如下:



    3、Shiro身份认证概述

    先来看一段简单的代码,shiro.ini为配置文件,类为用于说明流程的代码测试类:
    #shiro.ini
    [users]
    zhang=123
    wang=123

    @Test
    public void testHelloWorld() {
        //获取SecurityManager工厂,使用shiro.ini配置文件进行初始化
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
    
        //得到SecurityManager实例,并绑定给SecurityUtils
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
    
        //得到Subject及创建用户名/密码身份验证token(即用户身份/凭证)
        Subject subject = SecurityUtils.getSubject();
        AuthenticationToken token = new UsernamePasswordToken("zhang", "123");
    
        try {
            //登陆
            subject.login(token);
        } catch (AuthenticationException e) {
            //身份验证失败
            e.printStackTrace();
        }
    
        //断言用户已经登陆
        Assert.assertEquals(true, subject.isAuthenticated());
    
        //退出
        subject.logout();
    }

    可以看到,SecurityManager通过配置文件进行实例化(通过工厂类产出),该配置文件中简单配置了用户名和密码,实际上配置文件上还有很多内容可以配置诸如自定义的各种类等,都可以注入到Shiro中进行替换,此处就不再展开详述。

    SecurityManager 是 Shiro 的核心,这里把 SecurityManager 绑定到 SecurityUtils 中,只是为了方便后续调用一些方法。比如登陆方法 login(),看似是 subject.login() 在调用,实际上其内部也是调用了 SecurityManager 的 login() 方法。

    用户的登陆信息是封装到 AuthenticationToken 实现类中进行传递的,这里使用了 Shiro 中内置的一个简单实现类 UsernamePasswordToken,然后通过 login() 方法层层调用,最终用这个 token 做了下面的事情:
    • 确定 Realm 的数量,根据 Realm 是否单一来确定执行方法 doSingleRealmAuthentication() 或 doMultiRealmAuthentication()
    • 不论哪个方法都会要求通过 Realm 和 token(getAuthenticationInfo(AuthenticationToken token)) 返回认证信息 AuthenticationInfo
    • 而如何确定并返回这个信息,也即是确认用户登录信息和授权信息的过程,是由开发者自定义(如通过数据库抓取信息对比判断等)
    • 自定义 Realm 必须实现 Realm 接口,更简单快捷的方式是继承抽象类 AuthenticatingRealm
    • 继承 AuthenticatingRealm 则需要分别实现认证方法doGetAuthenticationInfo() 和 授权方法 doGetAuthorizationInfo()

    在 doGetAuthenticationInfo() 中还可以自定义密码匹配策略 CredentialsMatcher,将会进一步调用 assertCredentialsMatch() 进行密码匹配判定。当然,这些都是自行扩展,也由此 Shiro 的灵活程度可见一斑。


    4、Shiro 的权限控制

    Shiro 的权限控制是通过过滤器来实现的,所以其核心对象 ShiroFilter 就是整个 Shiro Web 中的门户,所有请求都会被 ShiroFilter 过滤并进行相应的链式处理。

    这个处理流程是这样的:
    • ShiroFilter 执行过滤器链
    • 通过原始过滤器链获取新的过滤器链
      • FilterChainResolver 解析 url,找到对应的新的 FilterChain 过滤器链
    • 执行新的过滤器链

    AbstractShiroFilter //如ShiroFilter/ SpringShiroFilter都继承该Filter
    • doFilter //Filter的doFilter
      • doFilterInternal //转调doFilterInternal
        • executeChain(request, response, chain) //执行过滤器链
          • FilterChain chain = getExecutionChain(request, response, origChain) //使用原始过滤器链获取新的过滤器链
            • chain.doFilter(request, response) //执行新组装的过滤器链

          • getExecutionChain(request, response, origChain) //获取过滤器链流程
            • FilterChainResolver resolver = getFilterChainResolver(); //获取相应的FilterChainResolver
            • FilterChain resolved = resolver.getChain(request, response, origChain); //通过FilterChainResolver根据当前请求解析到新的FilterChain过滤器链

    注:FilterChainResolver 的实现类中往往通过 FilterChainManager (核心属性 filters / filterChains)维护过滤器关系链

    4.1 默认过滤器

    Shiro 内部提供了一个路径匹配的 FilterChainResolver 实现:PathMatchingFilterChainResolver,它会解析 shiro.ini 配置文件中 [urls] 的url模式:
    [urls]
    #authc 需要通过身份验证
    #anon  匿名访问(即不需要登陆)
    #roles 有角色限制,如roles[admin]表示需要admin角色才能访问
    #perms 有权限限制,如perms["user:create"]表示要有"user:create"权限才能访问
    /login=anon
    /unauthorized=anon
    /static/**=anon
    /authenticated=authc
    /role=authc,roles[admin]
    /permission=authc,perms["user:create"]

    而 PathMatchingFilterChainResolver 内部通过 FilterChainManager 维护着过滤器链,比如 DefaultFilterChainManager 实现维护着url模式与过滤器链的关系。因此我们可以通过 FilterChainManager 进行动态增加url模式与过滤器链的关系。

    DefaultFilterChainManager 在实例化时会通过构造函数默认添加 DefaultFilter 中声明的过滤器:
    public DefaultFilterChainManager(FilterConfig filterConfig) {
    	this.filters = new LinkedHashMap<String, Filter>();
    	this.filterChains = new LinkedHashMap<String, NamedFilterList>();
    	setFilterConfig(filterConfig);
        //默认添加 DefaultFilter 中声明的过滤器
    	addDefaultFilters(true);
    }

    public enum DefaultFilter {
    
        anon(AnonymousFilter.class),
        authc(FormAuthenticationFilter.class),
        authcBasic(BasicHttpAuthenticationFilter.class),
        logout(LogoutFilter.class),
        noSessionCreation(NoSessionCreationFilter.class),
        perms(PermissionsAuthorizationFilter.class),
        port(PortFilter.class),
        rest(HttpMethodPermissionFilter.class),
        roles(RolesAuthorizationFilter.class),
        ssl(SslFilter.class),
        user(UserFilter.class);
        
    }

    4.2 自定义过滤器

    如果要注册自定义过滤器,IniSecurityManagerFactory / WebIniSecurityManagerFactory 在启动时会自动扫描ini配置文件中的 [filters] / [main] 部分并注册这些过滤器到 DefaultFilterChainManager;且创建相应的url模式与其过滤器关系链。

    在 DefaultFilterChainManager 中有两个属性 Map<String, Filter> filters 和 Map<String, NamedFilterList> filterChains,这意味着我们即可注入自定义的 “过滤器” 和 “过滤器关系链(匹配链,即什么url对应执行什么filter)”

    而我们自定义过滤器要做的两件事:
    • 完成自定义过滤器的编写
    • 将自定义过滤器注入到 filterChains 中去

    显然在读取配置文件 shiro.ini 时就将其中的 [filters] 部分的自定义过滤器载入了:
    [filters]  
    myFilter1=com.github.zhangkaitao.shiro.chapter8.web.filter.MyOncePerRequestFilter  
    myFilter2=com.github.zhangkaitao.shiro.chapter8.web.filter.MyAdviceFilter  
    [urls]  
    /**=myFilter1,myFilter2   

    实际上你会发现,基于Spring或者SpringBoot,这种套路也是类似的,不过是面向对象(面向Bean)而已:
    <!-- spring -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    	<property name="securityManager" ref="securityManager" />
    	<property name="loginUrl" value="/security/login.html" />
    	<property name="successUrl" value="/home.html" />
    	<property name="unauthorizedUrl" value="/security/unauthorized.html" />
    	<property name="filters">
    		<map>
    			<entry key="anyRoles" value-ref="anyRolesAuthorizationFilter" />
    		</map>
    	</property>
    	<property name="filterChainDefinitions">
    		<value>
    			/admin = anyRoles[admin1,admin2]
    			/** = anon
    		</value>
    	</property>
    </bean>

    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){
    	ShiroFilterFactoryBean shiroFilterFactoryBean  = new ShiroFilterFactoryBean();
    
    	//设置SecurityManager
    	shiroFilterFactoryBean.setSecurityManager(securityManager);
    	//自定义过滤器
    	Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
    	filtersMap.put("myAccessControlFilter", new MyAccessControlFilter());
    	shiroFilterFactoryBean.setFilters(filtersMap);
    
    	//过滤器
    	Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
    
    	filterChainDefinitionMap.put("/createPermission", "anon");
    	filterChainDefinitionMap.put("/**", "myAccessControlFilter");
    
    	shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    	return shiroFilterFactoryBean;
    }


    5、参考链接



  • 相关阅读:
    【转】VC 隐藏模块、MFC 改变窗口类名
    【转】Reason: The specified virtual disk needs repair.
    关于CreateProcess函数一些经验
    利用NtQuerySystemInformation函数遍历进程,遍历线程,获取线程挂起或运行状态
    uefi安装win7,deepin15双系统后grub没有windows选项
    X64下的虚拟地址到物理地址的转换
    WinDBG 技巧:列出模块(DLL/EXE)里面所有的符号(symbol)
    windows7内核分析之x86&x64第二章系统调用
    使用VS Code写PHP并进行调试
    使用CreateProcess创建新的process 并返回process运行结束返回值
  • 原文地址:https://www.cnblogs.com/deng-cc/p/9401900.html
Copyright © 2020-2023  润新知