shiro 订阅
Apache Shiro(读作“sheeroh”,即日语“城”)是一个开源安全框架,提供身份验证、授权、密码学和会话管理。Shiro框架直观、易用,同时也能提供健壮的安全性。 展开全文
Apache Shiro(读作“sheeroh”,即日语“城”)是一个开源安全框架,提供身份验证、授权、密码学和会话管理。Shiro框架直观、易用,同时也能提供健壮的安全性。
信息
外文名
Apache Shiro
分    类
编程代码
中文名
Apache Shiro
shiro历史
Shiro的前身是JSecurity。 [1]  2004年,Les Hazlewood和Jeremy Haile创办了Jsecurity。当时他们找不到适用于应用程序级别的合适Java安全框架,同时又对JAAS非常失望。2004年到2008年期间,JSecurity托管在SourceForge上,贡献者包括Peter Ledbrook、Alan Ditzel和Tim Veil。2008年,JSecurity项目贡献给了Apache软件基金会(ASF),并被接纳成为Apache Incubator项目,由导师管理,目标是成为一个顶级Apache项目。期间,Jsecurity曾短暂更名为Ki(读作“Key”),随后因商标问题被社群更名为“Shiro”。随后项目持续在Apache Incubator中孵化,并增加了贡献者Kalle Korhonen。2010年7月,Shiro社区发布了1.0版,随后社区创建了其项目管理委员会,并选举Les Hazlewood为主席。2010年9月22日,Shrio成为Apache软件基金会的顶级项目(TLP)。 [2] 
收起全文
精华内容
参与话题
问答
  • shiro详解-shiro史上最全学习笔记

    千次阅读 多人点赞 2018-12-13 15:54:16
    1.shiro简介 1.1.基本功能点 Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与 Web 集成、缓存等。其基本...

    1.shiro简介

    1.1.基本功能点

    Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与 Web 集成、缓存等。其基本功能点如下图所示:
    在这里插入图片描述

    • Authentication:身份认证 / 登录,验证用户是不是拥有相应的身份;
    • Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
    • Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;
    • Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
    • Web Support:Web 支持,可以非常容易的集成到 Web 环境;
    • Caching:缓存,比如用户登录后,其用户信息、拥有的角色 / 权限不必每次去查,这样可以提高效率;
    • Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
    • Testing:提供测试支持;
    • Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
    • Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

    记住一点,Shiro 不会去维护用户、维护权限;这些需要我们自己去设计 / 提供;然后通过相应的接口注入给 Shiro 即可。

    1.2.Shiro的架构

    1.2.1.外部

    我们从外部来看 Shiro ,即从应用程序角度的来观察如何使用 Shiro 完成工作。如下图:
    在这里插入图片描述

    可以看到:应用代码直接交互的对象是 Subject,也就是说 Shiro 的对外 API 核心就是 Subject;其每个 API 的含义:

    Subject:主体,代表了当前 “用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都Subject,如网络爬虫,机器人等;即一个抽象概念;所有 Subject 都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager;可以把 Subject 认为是一个门面;SecurityManager 才是实际的执行者;

    SecurityManager:安全管理器;即所有与安全有关的操作都会与 SecurityManager 交互;且它管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过 SpringMVC,你可以把它看成 DispatcherServlet 前端控制器;

    Realm:域,Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。

    也就是说对于我们而言,最简单的一个 Shiro 应用:

    1. 应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager;
    2. 我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。

    从以上也可以看出,Shiro 不提供维护用户 / 权限,而是通过 Realm 让开发人员自己注入。

    1.2.2.内部

    接下来我们来从 Shiro 内部来看下 Shiro 的架构,如下图所示:
    在这里插入图片描述
    Subject:主体,可以看到主体可以是任何可以与应用交互的 “用户”;

    SecurityManager:相当于 SpringMVC 中的 DispatcherServlet 或者 Struts2 中的 FilterDispatcher;是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。

    Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;

    Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;

    Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意:Shiro 不知道你的用户 / 权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;

    SessionManager:如果写过 Servlet 就应该知道 Session 的概念,Session 呢需要有人去管理它的生命周期,这个组件就是 SessionManager;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境、EJB 等环境;所有呢,Shiro 就抽象了一个自己的 Session 来管理主体与应用之间交互的数据;这样的话,比如我们在 Web 环境用,刚开始是一台 Web 服务器;接着又上了台 EJB 服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到 Memcached 服务器);

    SessionDAO:DAO 大家都用过,数据访问对象,用于会话的 CRUD,比如我们想把 Session 保存到数据库,那么可以实现自己的 SessionDAO,通过如 JDBC 写到数据库;比如想把 Session 放到 Memcached 中,可以实现自己的 Memcached SessionDAO;另外 SessionDAO 中可以使用 Cache 进行缓存,以提高性能;

    CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能

    Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密 / 解密的。

    2.shiro组件

    2.1.身份验证

    身份验证,即在应用中谁能证明他就是他本人。一般提供如他们的身份 ID 一些标识信息来表明他就是他本人,如提供身份证,用户名 / 密码来证明。

    在 shiro 中,用户需要提供 principals (身份)和 credentials(证明)给 shiro,从而应用能验证用户身份:

    principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。一个主体可以有多个 principals,但只有一个 Primary principals,一般是用户名 / 密码 / 手机号。

    credentials:证明 / 凭证,即只有主体知道的安全值,如密码 / 数字证书等。

    最常见的 principals 和 credentials 组合就是用户名 / 密码了。接下来先进行一个基本的身份认证。

    另外两个相关的概念是之前提到的 SubjectRealm,分别是主体及验证主体的数据源。

    2.1.1.maven依赖配置

    <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <shiro.version>1.2.2</shiro.version>
    </properties>
    
    <dependencies>
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
      </dependency>
      <!-- log4j -->
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.2</version>
      </dependency>
      <!-- shiro相关依赖 -->
      <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-web</artifactId>
        <version>${shiro.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-ehcache</artifactId>
        <version>${shiro.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>${shiro.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-quartz</artifactId>
        <version>${shiro.version}</version>
      </dependency>
      <dependency>
          <groupId>commons-logging</groupId>
          <artifactId>commons-logging</artifactId>
          <version>1.1.3</version>
        </dependency>
    </dependencies>
    

    2.1.2.登录/退出

    1、准备一些用户身份

    [users]
    zhang=123
    wang=123
    

    此处使用 ini 配置文件,通过 [users] 指定了两个主体:zhang/123、wang/123。

     @Test
        public void  testLoginLoginout(){
            //1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
            Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
            //2、得到SecurityManager实例 并绑定给SecurityUtils
            org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
            SecurityUtils.setSecurityManager(securityManager);
            //3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
            try {
                //4、登录,即身份验证
                subject.login(token);
            } catch (AuthenticationException e) {
                //5、身份验证失败
            }
            System.out.println(subject.isAuthenticated());
            //6、退出
            subject.logout();
    
        }
    
    • 首先通过 new IniSecurityManagerFactory 并指定一个 ini 配置文件来创建一个 SecurityManager 工厂;
    • 接着获取 SecurityManager 并绑定到 SecurityUtils,这是一个全局设置,设置一次即可;
    • 通过 SecurityUtils 得到 Subject,其会自动绑定到当前线程;如果在 web 环境在请求结束时需要解除绑定;然后获取身份验证的 Token,如用户名 / 密码;
    • 调用 subject.login 方法进行登录,其会自动委托给 SecurityManager.login 方法进行登录;
    • 如果身份验证失败请捕获 AuthenticationException 或其子类,常见的如: DisabledAccountException(禁用的帐号)、LockedAccountException(锁定的帐号)、UnknownAccountException(错误的帐号)、ExcessiveAttemptsException(登录失败次数过多)、IncorrectCredentialsException (错误的凭证)、ExpiredCredentialsException(过期的凭证)等,具体请查看其继承关系;对于页面的错误消息展示,最好使用如 “用户名 / 密码错误” 而不是 “用户名错误”/“密码错误”,防止一些恶意用户非法扫描帐号库;
    • 最后可以调用 subject.logout 退出,其会自动委托给 SecurityManager.logout 方法退出。

    从如上代码可总结出身份验证的步骤:

    1. 收集用户身份 / 凭证,即如用户名 / 密码;
    2. 调用 Subject.login 进行登录,如果失败将得到相应的 AuthenticationException 异常,根据异常提示用户错误信息;否则登录成功;
    3. 最后调用 Subject.logout 进行退出操作。

    2.1.3.身份认证流程

    在这里插入图片描述

    流程如下:

    1. 首先调用 Subject.login(token) 进行登录,其会自动委托给 Security Manager,调用之前必须通过 SecurityUtils.setSecurityManager() 设置;
    2. SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证;
    3. Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入自己的实现;
    4. Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;
    5. Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返回 / 抛出异常表示身份验证失败了。此处可以配置多个 Realm,将按照相应的顺序及策略进行访问。

    2.1.4.Realm

    Realm:域,Shiro 从从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。如我们之前的 ini 配置方式将使用 org.apache.shiro.realm.text.IniRealm。

    2.1.4.1.单 Realm 配置

    1、自定义 Realm 实现(需要实现四个方法)

    public class MyRealm extends AuthorizingRealm {
        /**
         * 获取角色与权限
         *doGetAuthorizationInfo执行时机有三个,如下:
         *  1、subject.hasRole(“admin”) 或 subject.isPermitted(“admin”):自己去调用这个是否有什么角色或者是否有什么权限的时候;
         *  2、@RequiresRoles("admin") :在方法上加注解的时候;
         *  3、@shiro.hasPermission name = "admin"/@shiro.hasPermission:"dustin:test"在页面上加shiro标签的时候,即进这个页面的时候扫描到有这个标签的时候。
         * @param principals
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            return null;
        }
    
        /**
         * 登录信息验证
         
         * 1.doGetAuthenticationInfo执行时机如下
         * 当调用Subject currentUser = SecurityUtils.getSubject();
         * currentUser.login(token);
         * @param token
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
            String username = (String)token.getPrincipal();
           String password = new String((char[])token.getCredentials());
    
            if(!"zhang".equals(username)) {
                throw new UnknownAccountException(); //如果用户名错误
            }
            if(!"123".equals(password)) {
                throw new IncorrectCredentialsException(); //如果密码错误
            }
    
    
           return new SimpleAuthenticationInfo(username, password, getName());
        }
    
    
        @Override
        public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
            super.clearCachedAuthorizationInfo(principals);
        }
    
        @Override
        public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
            super.clearCachedAuthenticationInfo(principals);
        }
    
        @Override
        public void clearCache(PrincipalCollection principals) {
            super.clearCache(principals);
        }
    
        public void clearAllCachedAuthorizationInfo() {
            getAuthorizationCache().clear();
        }
    
        public void clearAllCachedAuthenticationInfo() {
            getAuthenticationCache().clear();
        }
    
        public void clearAllCache() {
            clearAllCachedAuthenticationInfo();
            clearAllCachedAuthorizationInfo();
    
        }
    }
    
    
    

    2、ini 配置文件指定自定义 Realm 实现 (shiro-realm.ini)

    #声明一个realm
    myRealm=net.wanho.realm.MyRealm
    #指定securityManager的realms实现
    securityManager.realms=$myRealm
    

    3、测试

    只需要把之前的 shiro.ini 配置文件改成 shiro-realm.ini 即可。

     @Test
        public void  testLoginLoginout(){
            //1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
            Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-realm.ini");
            //2、得到SecurityManager实例 并绑定给SecurityUtils
            org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
            SecurityUtils.setSecurityManager(securityManager);
            //3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
            try {
                //4、登录,即身份验证
                subject.login(token);
            } catch (AuthenticationException e) {
                //5、身份验证失败
            }
            System.out.println(subject.isAuthenticated());
            //6、退出
            subject.logout();
    
        }
    

    2.1.4.2.多Realm 配置

    1、ini 配置文件(shiro-multi-realm.ini)

    #声明一个realm
    myRealm1=net.wanho.realm.MyRealm1
    myRealm2=net.wanho.realm.MyRealm2
    #指定securityManager的realms实现
    securityManager.realms=$myRealm1,$myRealm2
    

    注:securityManager 会按照 realms 指定的顺序进行身份认证。此处我们使用显示指定顺序的方式指定了 Realm 的顺序,如果删除 “securityManager.realms=myRealm1,myRealm2”,那么securityManager 会按照 realm 声明的顺序进行使用(即无需设置 realms 属性,其会自动发现),当我们显示指定 realm 后,其他没有指定 realm 将被忽略,如 “securityManager.realms=$myRealm1”,那么 myRealm2 不会被自动设置进去。

    2、测试

    测试方法同单 Realm 配置方法一致

    2.1.4.3.Shiro 默认提供的 Realm

    在这里插入图片描述

    其中主要默认实现如下:

    org.apache.shiro.realm.text.IniRealm:[users] 部分指定用户名 / 密码及其角色;[roles] 部分指定角色即权限信息;

    org.apache.shiro.realm.text.PropertiesRealm: user.username=password,role1,role2 指定用户名 / 密码及其角色;role.role1=permission1,permission2 指定角色及权限信息;

    org.apache.shiro.realm.jdbc.JdbcRealm:通过 sql 查询相应的信息,如 “select password from users where username = ?” 获取用户密码,“select password, password_salt from users where username = ?” 获取用户密码及盐;“select role_name from user_roles where username = ?” 获取用户角色;“select permission from roles_permissions where role_name = ?” 获取角色对应的权限信息;也可以调用相应的 api 进行自定义 sql;

    2.1.4.4.JDBC Realm 使用

    1、数据库及依赖

    使用 mysql 数据库及 druid 连接池

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.18</version>
    </dependency>
    <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>druid</artifactId>
          <version>1.0.11</version>
     </dependency>
    

    2、到数据库 shiro 下建三张表:users(用户名 / 密码)、user_roles(用户 / 角色)、roles_permissions(角色 / 权限),并添加一个用户记录,用户名 / 密码为 zhang/123;

    3、ini 配置(shiro-jdbc-realm.ini)

    jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
    dataSource=com.alibaba.druid.pool.DruidDataSource
    dataSource.driverClassName=com.mysql.jdbc.Driver
    dataSource.url=jdbc:mysql://localhost:3306/shiro
    dataSource.username=root
    dataSource.password=11111
    jdbcRealm.dataSource=$dataSource
    securityManager.realms=$jdbcRealm
    

    注:

    • 变量名 = 全限定类名会自动创建一个类实例 全限定类名会自动创建一个类实例
    • 变量名. 属性 = 值 自动调用相应的 setter 方法进行赋值
    • $ 变量名 引用之前的一个对象实例

    4、测试

     @Test
        public void  testLoginLoginout(){
            //1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
            Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-jdbc-realm.ini");
            //2、得到SecurityManager实例 并绑定给SecurityUtils
            org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
            SecurityUtils.setSecurityManager(securityManager);
            //3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
            try {
                //4、登录,即身份验证
                subject.login(token);
            } catch (AuthenticationException e) {
                //5、身份验证失败
            }
            System.out.println(subject.isAuthenticated());
            //6、退出
            subject.logout();
    
        }
    

    2.1.5.Authenticator 及 AuthenticationStrategy

    Authenticator 的职责是验证用户帐号,是 Shiro API 中身份验证核心的入口点,

    如果验证成功,将返回 AuthenticationInfo 验证信息;此信息中包含了身份及凭证;如果验证失败将抛出相应的AuthenticationException 实现。

    SecurityManager 接口继承了 Authenticator,另外还有一个 ModularRealmAuthenticator 实现,其委托给多个 Realm 进行验证,验证规则通过 AuthenticationStrategy 接口指定,默认提供的实现:

    FirstSuccessfulStrategy:只要有一个 Realm 验证成功即可,只返回第一个 Realm 身份验证成功的认证信息,其他的忽略;

    AtLeastOneSuccessfulStrategy:只要有一个 Realm 验证成功即可,和 FirstSuccessfulStrategy 不同,返回所有 Realm 身份验证成功的认证信息;

    AllSuccessfulStrategy:所有 Realm 验证成功才算成功,且返回所有 Realm 身份验证成功的认证信息,如果有一个失败就失败了。

    **注:**ModularRealmAuthenticator 默认使用 AtLeastOneSuccessfulStrategy 策略。

    假设我们有三个 realm:
    myRealm1: 用户名 / 密码为 zhang/123 时成功,且返回身份 / 凭据为 zhang/123;
    myRealm2: 用户名 / 密码为 wang/123 时成功,且返回身份 / 凭据为 wang/123;
    myRealm3: 用户名 / 密码为 zhang/123 时成功,且返回身份 / 凭据为 zhang@163.com/123,和 myRealm1 不同的是返回时的身份变了;

    1、ini 配置文件 (shiro-authenticator-all-success.ini)

    #指定securityManager的authenticator实现
    authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator
    securityManager.authenticator=$authenticator
    #指定securityManager.authenticator的authenticationStrategy
    allSuccessfulStrategy=org.apache.shiro.authc.pam.AllSuccessfulStrategy
    securityManager.authenticator.authenticationStrategy=$allSuccessfulStrategy
    
    myRealm=net.wanho.realm.MyRealm
    myRealm2=net.wanho.realm.MyRealm2
    myRealm3=net.wanho.realm.MyRealm3
    securityManager.realms=$myRealm,$myRealm3
    

    2、新建realm

    MyRealm2

    public class MyRealm2 extends AuthorizingRealm {
        /**
         * 获取角色与权限
         *doGetAuthorizationInfo执行时机有三个,如下:
         *  1、subject.hasRole(“admin”) 或 subject.isPermitted(“admin”):自己去调用这个是否有什么角色或者是否有什么权限的时候;
         *  2、@RequiresRoles("admin") :在方法上加注解的时候;
         *  3、@shiro.hasPermission name = "admin"/@shiro.hasPermission:"dustin:test"在页面上加shiro标签的时候,即进这个页面的时候扫描到有这个标签的时候。
         * @param principals
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            return null;
        }
    
        /**
         * 登录信息验证
         *
         * 1.doGetAuthenticationInfo执行时机如下
         * 当调用Subject currentUser = SecurityUtils.getSubject();
         * currentUser.login(token);
         * @param token
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
            String username = (String)token.getPrincipal();
           String password = new String((char[])token.getCredentials());
    
            if(!"wang".equals(username)) {
                throw new UnknownAccountException(); //如果用户名错误
            }
            if(!"123".equals(password)) {
                throw new IncorrectCredentialsException(); //如果密码错误
            }
    
    
           return new SimpleAuthenticationInfo(username, password, getName());
        }
    
    
        @Override
        public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
            super.clearCachedAuthorizationInfo(principals);
        }
    
        @Override
        public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
            super.clearCachedAuthenticationInfo(principals);
        }
    
        @Override
        public void clearCache(PrincipalCollection principals) {
            super.clearCache(principals);
        }
    
        public void clearAllCachedAuthorizationInfo() {
            getAuthorizationCache().clear();
        }
    
        public void clearAllCachedAuthenticationInfo() {
            getAuthenticationCache().clear();
        }
    
        public void clearAllCache() {
            clearAllCachedAuthenticationInfo();
            clearAllCachedAuthorizationInfo();
    
        }
    }
    
    
    

    MyRealm3

    public class MyRealm extends AuthorizingRealm {
        /**
         * 获取角色与权限
         *doGetAuthorizationInfo执行时机有三个,如下:
         *  1、subject.hasRole(“admin”) 或 subject.isPermitted(“admin”):自己去调用这个是否有什么角色或者是否有什么权限的时候;
         *  2、@RequiresRoles("admin") :在方法上加注解的时候;
         *  3、@shiro.hasPermission name = "admin"/@shiro.hasPermission:"dustin:test"在页面上加shiro标签的时候,即进这个页面的时候扫描到有这个标签的时候。
         * @param principals
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            return null;
        }
    
        /**
         * 登录信息验证
         *
         * 1.doGetAuthenticationInfo执行时机如下
         * 当调用Subject currentUser = SecurityUtils.getSubject();
         * currentUser.login(token);
         * @param token
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
            String username = (String)token.getPrincipal();
           String password = new String((char[])token.getCredentials());
    
            if(!"zhang".equals(username)) {
                throw new UnknownAccountException(); //如果用户名错误
            }
            if(!"123".equals(password)) {
                throw new IncorrectCredentialsException(); //如果密码错误
            }
    
    
           return new SimpleAuthenticationInfo("zhang@163.com", password, getName());
        }
    
    
        @Override
        public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
            super.clearCachedAuthorizationInfo(principals);
        }
    
        @Override
        public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
            super.clearCachedAuthenticationInfo(principals);
        }
    
        @Override
        public void clearCache(PrincipalCollection principals) {
            super.clearCache(principals);
        }
    
        public void clearAllCachedAuthorizationInfo() {
            getAuthorizationCache().clear();
        }
    
        public void clearAllCachedAuthenticationInfo() {
            getAuthenticationCache().clear();
        }
    
        public void clearAllCache() {
            clearAllCachedAuthenticationInfo();
            clearAllCachedAuthorizationInfo();
    
        }
    }
    
    
    

    3、测试

    • 首先通用化登录逻辑
    private void login(String configFile) {
        //1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
        Factory<org.apache.shiro.mgt.SecurityManager> factory =
                new IniSecurityManagerFactory(configFile);
        //2、得到SecurityManager实例 并绑定给SecurityUtils
        org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        //3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
        subject.login(token);
    }
    
      /**
         * 测试验证规则(可在配置文件中修改)
         *
         * FirstSuccessfulStrategy:只要有一个 Realm 验证成功即可,只返回第一个 Realm 身份验证成功的认证信息,其他的忽略;
            AtLeastOneSuccessfulStrategy:只要有一个 Realm 验证成功即可,和 FirstSuccessfulStrategy 不同,返回所有 Realm 身份验证成功的认证信息;
            AllSuccessfulStrategy:所有 Realm 验证成功才算成功,且返回所有 Realm 身份验证成功的认证信息,如果有一个失败就失败了。
    
         */
        @Test
        public void testAllSuccessfulStrategyWithSuccess() {
            login("classpath:shiro-authenticator-all-success.ini");
            Subject subject = SecurityUtils.getSubject();
            //得到一个身份集合,其包含了Realm验证成功的身份信息
            PrincipalCollection principalCollection = subject.getPrincipals();
            for (Object principal : principalCollection) {
                System.out.println(principal.toString());
            }
        }
    
    

    4、自定义AuthenticationStrategy实现,首先看其API:

    //在所有Realm验证之前调用  
    AuthenticationInfo beforeAllAttempts(  
    Collection<? extends Realm> realms, AuthenticationToken token)   
    throws AuthenticationException;  
    //在每个Realm之前调用  
    AuthenticationInfo beforeAttempt(  
    Realm realm, AuthenticationToken token, AuthenticationInfo aggregate)   
    throws AuthenticationException;  
    //在每个Realm之后调用  
    AuthenticationInfo afterAttempt(  
    Realm realm, AuthenticationToken token,   
    AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t)  
    throws AuthenticationException;  
    //在所有Realm之后调用  
    AuthenticationInfo afterAllAttempts(  
    AuthenticationToken token, AuthenticationInfo aggregate)   
    throws AuthenticationException;   
    

    自定义实现时一般继承org.apache.shiro.authc.pam.AbstractAuthenticationStrategy即可

    2.2.授权

    授权,也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。

    主体
    主体,即访问应用的用户,在 Shiro 中使用 Subject 代表该用户。用户只有授权后才允许访问相应的资源。

    资源
    在应用中用户可以访问的任何东西,比如访问 JSP 页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。

    权限
    安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如: 访问用户列表页面
    查看/新增/修改/删除用户数据(即很多时候都是 CRUD(增查改删)式权限控制)
    打印文档等等。。。

    如上可以看出,权限代表了用户有没有操作某个资源的权利,即反映在某个资源上的操作允不允许,不反映谁去执行这个操作。所以后续还需要把权限赋予给用户,即定义哪个用户允许在某个资源上做什么操作(权限),Shiro 不会去做这件事情,而是由实现人员提供。

    Shiro 支持粗粒度权限(如用户模块的所有权限)和细粒度权限(操作某个用户的权限,即实例级别的),后续部分介绍。

    角色
    角色代表了操作集合,可以理解为权限的集合,一般情况下我们会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的角色拥有一组不同的权限。

    隐式角色
    即直接通过角色来验证用户有没有操作权限,如在应用中 CTO、技术总监、开发工程师可以使用打印机,假设某天不允许开发工程师使用打印机,此时需要从应用中删除相应代码;再如在应用中 CTO、技术总监可以查看用户、查看权限;突然有一天不允许技术总监查看用户、查看权限了,需要在相关代码中把技术总监角色从判断逻辑中删除掉;即粒度是以角色为单位进行访问控制的,粒度较粗;如果进行修改可能造成多处代码修改。

    显示角色
    在程序中通过权限控制谁能访问某个资源,角色聚合一组权限集合;这样假设哪个角色不能访问某个资源,只需要从角色代表的权限集合中移除即可;无须修改多处代码;即粒度是以资源/实例为单位的;粒度较细。

    2.2.1.授权方式

    Shiro 支持三种方式的授权:

    1.编程式:通过写 if/else 授权代码块完成:

    Subject subject = SecurityUtils.getSubject();
    if(subject.hasRole(“admin”)) {
        //有权限
    } else {
        //无权限
    };
    

    2.注解式:通过在执行的 Java 方法上放置相应的注解完成:

    @RequiresRoles("admin")
    public void hello() {
        //有权限
    };
    

    3.SP/GSP 标签:在 JSP/GSP 页面通过相应的标签完成:

    <shiro:hasRole name="admin">
    <!— 有权限 —>
    </shiro:hasRole>;
    

    2.2.1.1.基于角色的访问控制(隐式角色)

    1、在 ini 配置文件配置用户拥有的角色(shiro-role.ini)

    [users]
    zhang=123,role1,role2
    wang=123,role1
    

    规则即:“用户名=密码,角色1,角色2”,如果需要在应用中判断用户是否有相应角色,就需要在相应的 Realm 中返回角色信息,也就是说 Shiro 不负责维护用户-角色信息,需要应用提供,Shiro 只是提供相应的接口方便验证。

    2、测试

    登录方法

    private Subject login(String configFile,String userName,String password) {
        //1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
        Factory<org.apache.shiro.mgt.SecurityManager> factory =
                new IniSecurityManagerFactory(configFile);
        //2、得到SecurityManager实例 并绑定给SecurityUtils
        org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        //3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
        subject.login(token);
        return subject;
    
    }
    

    Shiro提供了hasRole/hasRole用于判断用户是否拥有某个角色/某些权限;

     @Test
        public void testHasRole() {
            Subject subject = login("classpath:shiro-role.ini", "zhang", "123");
            //判断拥有角色:role1
            System.out.println(subject.hasRole("role1"));
            //判断拥有角色:role1 and role2
            System.out.println(subject.hasAllRoles(Arrays.asList("role1", "role2")));
            //判断拥有角色:role1 and role2 and !role3
            boolean[] result = subject.hasRoles(Arrays.asList("role1", "role2", "role3"));
            System.out.println(result[0]);
            System.out.println(result[1]);
            System.out.println(result[2]);
    
        }
    

    Shiro提供的checkRole/checkRoles和hasRole/hasAllRoles不同的地方是它在判断为假的情况下会抛出UnauthorizedException异常。

    @Test(expected = UnauthorizedException.class)
        public void testCheckRole() {
            Subject subject = login("classpath:shiro-role.ini", "zhang", "123");
            //判断是否拥有角色:role1
            subject.checkRole("role1");
            //判断是否拥有角色:role1 and role3 失败抛出异常
            subject.checkRoles("role1", "role3");
        }
    

    **注:**到此基于角色的访问控制(即隐式角色)就完成了,这种方式的缺点就是如果很多地方进行了角色判断,但是有一天不需要了那么就需要修改相应代码把所有相关的地方进行删除;这就是粗粒度造成的问题。

    2.2.1.2.基于角色的访问控制(显式角色)

    1、在ini配置文件配置用户拥有的角色及角色-权限关系(shiro-permission.ini)

    [users]  
    zhang=123,role1,role2  
    wang=123,role1  
    [roles]  
    role1=user:create,user:update  
    role2=user:create,user:delete  
    

    规则:“用户名=密码,角色1,角色2”“角色=权限1,权限2”,即首先根据用户名找到角色,然后根据角色再找到权限;即角色是权限集合;Shiro同样不进行权限的维护,需要我们通过Realm返回相应的权限信息。只需要维护“用户——角色”之间的关系即可。

    2、测试

    Shiro提供了isPermitted和isPermittedAll用于判断用户是否拥有某个权限或所有权限

      
    @Test
        public void testIsPermitted() {
            Subject subject = login("classpath:shiro-permission.ini", "zhang", "123");
            //判断拥有权限:user:create
            System.out.println(subject.isPermitted("user:create"));
            //判断拥有权限:user:update and user:delete
            System.out.println(subject.isPermittedAll("user:update", "user:delete"));
            //判断没有权限:user:view
            System.out.println(subject.isPermitted("user:view"));
        }
    
    @Test(expected = UnauthorizedException.class)  
    public void testCheckPermission () {  
        Subject subject = login("classpath:shiro-permission.ini", "zhang", "123");
        //断言拥有权限:user:create  
        subject.checkPermission("user:create");  
        //断言拥有权限:user:delete and user:update  
        subject.checkPermissions("user:delete", "user:update");  
        //断言拥有权限:user:view 失败抛出异常  
        subject.checkPermissions("user:view");  
    }   
    

    失败的情况下会抛出UnauthorizedException异常

    到此基于资源的访问控制(显示角色)就完成了,也可以叫基于权限的访问控制,这种方式的一般规则是“资源标识符:操作”,即是资源级别的粒度;这种方式的好处就是如果要修改基本都是一个资源级别的修改,不会对其他模块代码产生影响,粒度小。但是实现起来可能稍微复杂点,需要维护“用户——角色,角色——权限(资源:操作)”之间的关系。

    2.2.2.Permission权限

    字符串通配符权限

    规则:“资源标识符:操作:对象实例ID” 即对哪个资源的哪个实例可以进行什么操作。其默认支持通配符权限字符串,“:”表示资源/操作/实例的分割;“,”表示操作的分割;“*”表示任意资源/操作/实例。

    2.2.2.1.单个资源单个权限

    subject.checkPermissions("system:user:update");  
    

    用户拥有资源“system:user”的“update”权限。

    2.2.2.2.单个资源多个权限

    1.ini配置文件 (两种写法)

    role41=system:user:update,system:user:delete 
    #或者
    role41="system:user:update,delete"
    

    2.然后通过如下代码判断

    subject.checkPermissions("system:user:update", "system:user:delete");
    //或者
    subject.checkPermissions("system:user:update,delete");
    

    通过“system:user:update,delete”验证"system:user:update, system:user:delete"是没问题的,但是反过来是规则不成立。

    2.2.2.3.单个资源全部权限

    1.ini配置文件 (两种写法)

    role51="system:user:create,update,delete,view"
    #或者
    role51=system:user:*  
    

    2.然后通过如下代码判断

    subject.checkPermissions("system:user:create,delete,update:view");
    //或者
    subject.checkPermissions("system:user:*");  
    subject.checkPermissions("system:user");  
    

    通过“system:user:*”验证“system:user:create,delete,update:view”可以,但是反过来是不成立的。

    2.2.2.4.所有资源全部权限

    1.ini配置文件

    role61=*:view   
    

    2.然后通过如下代码判断

    subject.checkPermissions("user:view");  
    

    用户拥有所有资源的“view”所有权限。假设判断的权限是“"system:user:view”,那么需要“role5=* ?:view”这样写才行。

    2.2.2.5.单个实例单个权限

    1.ini配置文件

    role71=user:view:1  
    

    对资源user的1实例拥有view权限。

    2.然后通过如下代码判断

    subject.checkPermissions("user:view:1");  
    

    2.2.2.6.单个实例多个权限

    1.ini配置文件

    role72="user:update,delete:1" 
    

    2.然后通过如下代码判断

    subject.checkPermissions("user:delete,update:1");  
    subject.checkPermissions("user:update:1", "user:delete:1"); 
    

    2.2.2.7.单个实例所有权限

    1.ini配置文件

    role73=user:*:1 
    

    2.然后通过如下代码判断

    subject.checkPermissions("user:update:1", "user:delete:1", "user:view:1");  
    

    2.2.2.8.所有实例单个权限

    1.ini配置文件

    role74=user:auth:*
    

    2.然后通过如下代码判断

    subject.checkPermissions("user:auth:1", "user:auth:2");   
    

    2.2.2.9.所有实例所有权限

    1.ini配置文件

    role75=user:*:*  
    

    2.然后通过如下代码判断

    subject.checkPermissions("user:view:1", "user:auth:2");   
    

    2.2.2.10.Shiro对权限字符串缺失部分的处理

    如“user:view”等价于“user:view:”;而“organization”等价于“organization:”或者“organization::”。可以这么理解,这种方式实现了前缀匹配。
    另外如“user:”可以匹配如“user:delete”、“user:delete”可以匹配如“user:delete:1”、“user::1”可以匹配如“user:view:1”、“user”可以匹配“user:view”或“user:view:1”等。即可以匹配所有,不加可以进行前缀匹配;但是如“:view”不能匹配“system:user:view”,需要使用“::view”,即后缀匹配必须指定前缀(多个冒号就需要多个来匹配)。

    2.2.2.11.性能问题

    通配符匹配方式比字符串相等匹配来说是更复杂的,因此需要花费更长时间,但是一般系统的权限不会太多,且可以配合缓存来提供其性能,如果这样性能还达不到要求我们可以实现位操作算法实现性能更好的权限匹配。另外实例级别的权限验证如果数据量太大也不建议使用,可能造成查询权限及匹配变慢。可以考虑比如在sql查询时加上权限字符串之类的方式在查询时就完成了权限匹配。

    2.2.3.授权流程

    在这里插入图片描述

    流程如下:
    1、首先调用Subject.isPermitted*/hasRole接口,其会委托给SecurityManager,而SecurityManager接着会委托给Authorizer;
    2、Authorizer是真正的授权者,如果我们调用如isPermitted(“user:view”),其首先会通过PermissionResolver把字符串转换成相应的Permission实例;
    3、在进行授权之前,其会调用相应的Realm获取Subject相应的角色/权限用于匹配传入的角色/权限;
    4、Authorizer会判断Realm的角色/权限是否和传入的匹配,如果有多个Realm,会委托给ModularRealmAuthorizer进行循环判断,如果匹配如isPermitted
    /hasRole*会返回true,否则返回false表示授权失败。

    ModularRealmAuthorizer进行多Realm匹配流程:
    1、首先检查相应的Realm是否实现了实现了Authorizer;
    2、如果实现了Authorizer,那么接着调用其相应的isPermitted*/hasRole*接口进行匹配;
    3、如果有一个Realm匹配那么将返回true,否则返回false。

    如果Realm进行授权的话,应该继承AuthorizingRealm,其流程是:
    1.1、如果调用hasRole*,则直接获取AuthorizationInfo.getRoles()与传入的角色比较即可;
    1.2、首先如果调用如isPermitted(“user:view”),首先通过PermissionResolver将权限字符串转换成相应的Permission实例,默认使用WildcardPermissionResolver,即转换为通配符的WildcardPermission;
    2、通过AuthorizationInfo.getObjectPermissions()得到Permission实例集合;通过AuthorizationInfo. getStringPermissions()得到字符串集合并通过PermissionResolver解析为Permission实例;然后获取用户的角色,并通过RolePermissionResolver解析角色对应的权限集合(默认没有实现,可以自己提供);
    3、接着调用Permission. implies(Permission p)逐个与传入的权限比较,如果有匹配的则返回true,否则false。

    更多资深讲师相关课程资料、学习笔记请入群后向管理员免费获取,更有专业知识答疑解惑。入群即送价值499元在线课程一份。
    QQ群号:560819979
    敲门砖(验证信息):雨打蕉

    展开全文
  • shiro权限安全管理框架

    千人学习 2019-03-04 20:27:40
    Apache Shiro是一个强大且易用的Java安全框架,提供了认证、授权、加密和会话管理等功能。Shiro都可以轻松为其提供全面的安全管理服务,相比业内spring security 而言,Shiro显得更加小巧,应用也更加广泛。本课程...
  • Shiro详解

    千次阅读 多人点赞 2018-08-07 08:21:31
    Shiro 简介 简介 • Apache Shiro 是 Java 的一个安全(权限)框架。 • Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE 环境,也可以用在 JavaEE 环境。 • Shiro 可以完成:认证、授权、加密、...

    Shiro 简介


    简介
    • Apache Shiro 是 Java 的一个安全(权限)框架。
    • Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE 环境,也可以用在 JavaEE 环境。
    • Shiro 可以完成:认证、授权、加密、会话管理、与Web 集成、缓存等。
    • 下载:http://shiro.apache.org/


    功能简介


    • 基本功能点如下图所示:


    功能简介
    • Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
    • Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能进行什么操作,如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
    • Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境,也可以是 Web 环境的;
    • Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
    • Web Support:Web 支持,可以非常容易的集成到Web 环境;
    • Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
    • Concurrency:Shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能
    • 把权限自动传播过去;
    • Testing:提供测试支持;
    • Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
    • Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了


    Shiro 架构 (Shiro外部来看)


    • 从外部来看Shiro ,即从应用程序角度的来观察如何使用 Shiro 完成工作:


    Shiro 架构


    • Subject:应用代码直接交互的对象是 Subject,也就是说 Shiro 的对外API 核心就是 Subject。Subject 代表了当前“用户”, 这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等;与 Subject 的所有交互都会委托给 SecurityManager;Subject 其实是一个门面,SecurityManager 才是实际的执行者;
    • SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager 交互;且其管理着所有 Subject;可以看出它是 Shiro的核心,它负责与 Shiro 的其他组件进行交互,它相当于 SpringMVC 中DispatcherServlet 的角色
    • Realm:Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色/权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource


    Shiro 架构 (Shiro内部来看)


    Shiro 架构
    • Subject:任何可以与应用交互的“用户”;
    • SecurityManager :相当于SpringMVC 中的 DispatcherServlet;是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证、授权、会话及缓存的管理。
    • Authenticator:负责 Subject 认证,是一个扩展点,可以自定义实现;可以使用认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
    • Authorizer:授权器、即访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
    • Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC 实现,也可以是内存实现等等;由用户提供;所以一般在应用中都需要实现自己的 Realm;
    • SessionManager:管理 Session 生命周期的组件;而 Shiro 并不仅仅可以用在 Web环境,也可以用在如普通的 JavaSE 环境
    • CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少改变,放到缓存中后可以提高访问的性能
    • Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密/解密。


    搭建开发环境


    • 加入如下 jar 包:
    – shiro-all-1.3.2.jar
    – log4j-1.2.15.jar
    – slf4j-api-1.6.1.jar
    – slf4j-log4j12-1.6.1.jar
    • 加入 Spring 和 Shiro 的 jar 包
    • 配置 Spring 及 SpringMVC
     

    与Web 集成


    • Shiro 提供了与 Web 集成的支持,其通过一个ShiroFilter 入口来拦截需要安全控制的URL,然后进行相应的控制
    • ShiroFilter 类似于如 Strut2/SpringMVC 这种web 框架的前端控制器,是安全控制的入口点,其负责读取配置(如ini 配置文件),然后判断URL是否需要登录/权限等工作。


    ShiroFilter 的工作原理



    ShiroFilter

    DelegatingFilterProxy 作用是自动到 Spring 容器查找名字为 shiroFilter(filter-name)的 bean 并把所有 Filter的操作委托给它。



    部分细节
    • [urls] 部分的配置,其格式是: “url=拦截器[参数],拦截器[参数]”;
    • 如果当前请求的 url 匹配 [urls] 部分的某个url 模式,将会执行其配置的拦截器。
    • anon(anonymous) 拦截器表示匿名访问(即不需要登录即可访问)
    • authc (authentication)拦截器表示需要身份认证通过后才能访问shiro中默认的过滤器

    shiro中默认的过滤器

     

     

    • URL 匹配模式

    • url 模式使用 Ant 风格模式
    • Ant 路径通配符支持 ?、*、**,注意通配符匹配不包括目录分隔符“/”:
    – ?:匹配一个字符,如 /admin? 将匹配 /admin1,但不匹配 /admin 或 /admin/;
    – *:匹配零个或多个字符串,如 /admin 将匹配 /admin、admin123,但不匹配 /admin/1;
    – **:匹配路径中的零个或多个路径,如 /admin/** 将匹配 /admin/a 或 /admin/a/b

    • URL 匹配顺序

    •  URL  权限采取第一次匹配优先的 方式,即从头开始使用第一个匹配的 url 模式对应的拦截器链。
    • 如:
    – /bb/**=filter1
    – /bb/aa=filter2
    – /**=filter3
    – 如果请求的url是“/bb/aa”,因为按照声明顺序进行匹配,那么将使用 filter1 进行拦截。
     


    Shiro 架构 (Shiro外部来看)


    • 从外部来看Shiro ,即从应用程序角度的来观察如何使用 Shiro 完成工作:身份验证
    • 身份验证:一般需要提供如身份 ID 等一些标识信息来表明登录者的身份,如提供 email,用户名/密码来证明。
    • 在 shiro 中,用户需要提供 principals (身份)和 credentials(证明)给 shiro,从而应用能验证用户身份:
    • principals:身份,即主体的标识属性,可以是任何属性,如用户名、邮箱等,唯一即可。一个主体可以有多个 principals,但只有一个Primary principals,一般是用户名/邮箱/手机号。
    • credentials:证明/凭证,即只有主体知道的安全值,如密码/数字证书等。
    • 最常见的 principals 和 credentials 组合就是用户名/密码了

    身份验证基本流程


    • 1、收集用户身份/凭证,即如用户名/密码
    • 2、调用 Subject.login 进行登录,如果失败将得到相应的 AuthenticationException 异常,根据异常提示用户错误信息;否则登录成功
    • 3、创建自定义的 Realm 类,继承org.apache.shiro.realm.AuthorizingRealm 类,实现doGetAuthenticationInfo() 方法

     

    身份验证示例


    AuthenticationException


    • 如果身份验证失败请捕获 AuthenticationException 或其子类
    • 最好使用如“用户名/密码错误”而不是“用户名错误”/“密码错误”,防止一些恶意用户非法扫描帐号库;


    认证流程


    身份认证流程


    • 1、首先调用 Subject.login(token) 进行登录,其会自动委托给SecurityManager
    • 2、SecurityManager 负责真正的身份验证逻辑;它会委托给Authenticator 进行身份验证;
    • 3、Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入自己的实现;
    • 4、Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用
    AuthenticationStrategy 进行多 Realm 身份验证;
    • 5、Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返回/抛出异常表示身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。


    Realm


    • Realm:Shiro 从 Realm 获取安全数据(如用户、角色、权限),即 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作
    • Realm接口如下:


    Realm


    • 一般继承 AuthorizingRealm(授权)即可;其继承了AuthenticatingRealm(即身份验证),而且也间接继承了CachingRealm(带有缓存实现)。
    • Realm 的继承关系:


    Authenticator


    • Authenticator 的职责是验证用户帐号,是 Shiro API 中身份验证核心的入口点:如果验证成功,将返回AuthenticationInfo 验
    证信息;此信息中包含了身份及凭证;如果验证失败将抛出相应的 AuthenticationException 异常
    • SecurityManager 接口继承了 Authenticator,另外还有一个ModularRealmAuthenticator实现,其委托给多个Realm 进行
    验证,验证规则通过 AuthenticationStrategy 接口指定


    AuthenticationStrategy


    • AuthenticationStrategy 接口的默认实现:
    • FirstSuccessfulStrategy:只要有一个 Realm 验证成功即可,只返回第一个 Realm 身份验证成功的认证信息,其他的忽略;
    • AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,和FirstSuccessfulStrategy 不同,将返回所有Realm身份验证成功的认证信息;
    • AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有Realm身份验证成功的认证信息,如果有一个失败就失败了。
    • ModularRealmAuthenticator 默认是 AtLeastOneSuccessfulStrategy


    授权


    • 授权,也叫访问控制,即在应用中控制谁访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。
    • 主体(Subject):访问应用的用户,在 Shiro 中使用 Subject 代表该用户。用户只有授权后才允许访问相应的资源。
    • 资源(Resource):在应用中用户可以访问的 URL,比如访问 JSP 页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。
    • 权限(Permission):安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如:访问用户列表页面查看/新增/修改/删除用户数据(即很多时候都是CRUD(增查改删)式权限控制)等。权限代表了用户有没有操作某个资源的权利,即反映在某个资源上的操作允不允许。
    • Shiro 支持粗粒度权限(如用户模块的所有权限)和细粒度权限(操作某个用户的权限,即实例级别的)
    • 角色(Role):权限的集合,一般情况下会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的角色拥有一组不同的权限。


    授权方式


    • Shiro 支持三种方式的授权:
    – 编程式:通过写if/else 授权代码块完成
    – 注解式:通过在执行的Java方法上放置相应的注解完成,没有权限将抛出相应的异常
    – JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成


    默认拦截器


    • Shiro 内置了很多默认的拦截器,比如身份验证、授权等相关的。默认拦截器可以参考org.apache.shiro.web.filter.mgt.DefaultFilter中的枚举
    拦截器:


    身份验证相关的


    授权相关的


    其他


    Permissions


    • 规则: 资源 标识符:操作:对象实例 例  ID 即对哪个资源的哪个实例可以进行什么操作. 其 默认支持通配符权限字符串,:  表
    示 资源/ / 操作/ / 实例的分割;,  表示 操作的 分割,*  表示 任意资源/ / 操作/ / 实例。
    •  多层次管理:
    – 例如:user:query、user:edit
    –  冒号 是一个特殊字符,它用来分隔权限字符串的下一 部件:第一部分是权限被操作的领域(打印机),第二部分是被执行的操作。
    – 多个值: 每个 部件能够保护多个值。因此,除了授予用户 user:query和 user:edit 权限外,也 可以 简单地授予他们一 个: user: query , edit
    – 还可以用 用 *  号 代替所有的值,如:user:* , 也可以写:*:query,表示某个用户在所有的领域都有 query 的权限


    Shiro 的 Permissions


    •  实例 级访问控制
    – 这种情况通常会使用三个部件: 域、操作、被付诸实施的实例。如:user:edit:manager
    – 也 可以使用通配符来定义,如:user:edit:*、user:*:*、user:*:manager
    –  部分 省略 通配符:缺少的部件意味着用户可以访问所有与之匹配的值,比如:user:edit 等价于 user:edit :*、user 等价于 user:*:*
    – 注意: 通配符只能 从字符串的结尾处省略部件,也就是说 user:edit 并不等价于 user:*:edit


    授权流程



    • 流程如下:
    • 1、首先调用 Subject.isPermitted*/hasRole* 接口,其会委托给SecurityManager,而 SecurityManager 接着会委托给 Authorizer;
    • 2、Authorizer是真正的授权者,如果调用如isPermitted(“user:view”),其首先会通过
    • PermissionResolver 把字符串转换成相应的 Permission 实例;
    • 3、在进行授权之前,其会调用相应的 Realm 获取 Subject 相应的角色/权限用于匹配传入的角色/权限;
    • 4、Authorizer 会判断 Realm 的角色/权限是否和传入的匹配,如果有多个Realm,会委托给 ModularRealmAuthorizer 进行循环判断,如果匹配如 isPermitted*/hasRole* 会返回true,否则返回false表示授权失败。


    ModularRealmAuthorizer


    • ModularRealmAuthorizer 进行多 Realm 匹配流程:
    – 1、首先检查相应的 Realm 是否实现了实现了Authorizer;
    – 2、如果实现了 Authorizer,那么接着调用其相应的isPermitted*/hasRole* 接口进行匹配;
    – 3、如果有一个Realm匹配那么将返回 true,否则返回 false。


    Shiro 标签


    • Shiro 提供了 JSTL 标签用于在 JSP 页面进行权限控制,如根据登录用户显示相应的页面按钮。
    • guest 标签:用户没有身份验证时显示相应信息,即游客访问信息:


    • user 标签:用户已经经过认证/记住我登录后显示相应的信息。


    Shiro 标签


    • authenticated 标签:用户已经身份验证通过,即Subject.login登录成功,不是记住我登录的


    • notAuthenticated 标签:用户未进行身份验证,即没有调用Subject.login进行登录,包括记住我自动登录的也属于
    未进行身份验证。


    • pincipal 标签:显示用户身份信息,默认调用Subject.getPrincipal() 获取,即 Primary Principal。


    • hasRole 标签:如果当前 Subject 有角色将显示 body 体内容:


    • hasAnyRoles 标签:如果当前Subject有任意一个角色(或的关系)将显示body体内容。


    • lacksRole:如果当前 Subject 没有角色将显示 body 体内容


    • hasPermission:如果当前 Subject 有权限将显示 body 体内容


    • lacksPermission:如果当前Subject没有权限将显示body体内容。


    权限注解


    • @RequiresAuthentication:表示当前Subject已经通过login进行了身份验证;即 Subject. isAuthenticated() 返回 true
    • @RequiresUser:表示当前 Subject 已经身份验证或者通过记住我登录的。
    • @RequiresGuest:表示当前Subject没有身份验证或通过记住我登录过,即是游客身份。
    • @RequiresRoles(value={“admin”, “user”}, logical=Logical.AND):表示当前 Subject 需要角色 admin 和user
    • @RequiresPermissions (value={“user:a”, “user:b”},logical= Logical.OR):表示当前 Subject 需要权限 user:a 或user:b。


    自定义拦截器


    • 通过自定义拦截器可以扩展功能,例如:动态url-角色/权限访问控制的实现、根据 Subject 身份信息获取用户信息绑定到 Request(即设置通用数据)、验证码验证、在线用户信息的保存等


    会话管理


    概述
    • Shiro 提供了完整的企业级会话管理功能,不依赖于底层容器(如web容器tomcat),不管 JavaSE 还是 JavaEE 环境
    都可以使用,提供了会话管理、会话事件监听、会话存储/持久化、容器无关的集群、失效/过期支持、对Web 的透明支持、SSO 单点登录的支持等特性。


    会话相关的 API


    • Subject.getSession():即可获取会话;其等价于Subject.getSession(true),即如果当前没有创建 Session 对象会创建
    一个;Subject.getSession(false),如果当前没有创建 Session 则返回null
    • session.getId():获取当前会话的唯一标识
    • session.getHost():获取当前Subject的主机地址
    • session.getTimeout() & session.setTimeout(毫秒):获取/设置当前Session的过期时间
    • session.getStartTimestamp() & session.getLastAccessTime():获取会话的启动时间及最后访问时间;如果是 JavaSE 应用需要自己定期调用 session.touch() 去更新最后访问时间;如果是Web 应用,每次进入 ShiroFilter 都会自动调用 session.touch() 来更新最后访问时间。
    • session.touch() & session.stop():更新会话最后访问时间及销毁会话;当Subject.logout()时会自动调用 stop 方法来销毁会话。如果在web中,调用 HttpSession. invalidate()也会自动调用Shiro Session.stop 方法进行销毁Shiro 的会话
    • session.setAttribute(key, val) &session.getAttribute(key) &session.removeAttribute(key):设置/获取/删除会话属性;在整个会话范围内都可以对这些属性进行操作


    会话监听器


    • 会话监听器用于监听会话创建、过期及停止事件

    SessionDao


    • AbstractSessionDAO 提供了 SessionDAO 的基础实现,如生成会话ID等
    • CachingSessionDAO 提供了对开发者透明的会话缓存的功能,需要设置相应的 CacheManager
    • MemorySessionDAO 直接在内存中进行会话维护
    • EnterpriseCacheSessionDAO 提供了缓存功能的会话维护,默认情况下使用 MapCache 实现,内部使用ConcurrentHashMap 保存缓存的会话。
    配置示例


    配置示例


    数据表
    • create table sessions ( id varchar(200),session varchar(2000),constraint pk_sessions primary key(id)) charset=utf8 ENGINE=InnoDB;


    Session Dao:


    Session Dao:


    SerializableUtils:


    会话验证


    • Shiro 提供了会话验证调度器,用于定期的验证会话是否已过期,如果过期将停止会话
    • 出于性能考虑,一般情况下都是获取会话时来验证会话是否过期并停止会话的;但是如在 web 环境中,如果用户不主动退出是不知道会话是否过期的,因此需要定期的检测会话是否过期,Shiro 提供了会话验证调度器SessionValidationScheduler
    • Shiro 也提供了使用Quartz会话验证调度器:QuartzSessionValidationScheduler


    缓存


    CacheManagerAware 接口


    • Shiro 内部相应的组件(DefaultSecurityManager)会自动检测相应的对象(如Realm)是否实现了
    CacheManagerAware 并自动注入相应的CacheManager。


    Realm 缓存


    • Shiro 提供了 CachingRealm,其实现了CacheManagerAware 接口,提供了缓存的一些基础实现;
    • AuthenticatingRealm 及 AuthorizingRealm 也分别提供了对AuthenticationInfo 和 AuthorizationInfo 信息的缓存。


    Session 缓存


    • 如 SecurityManager 实现了 SessionSecurityManager,其会判断 SessionManager 是否实现了CacheManagerAware 接口,如果实现了会把CacheManager 设置给它。
    • SessionManager 也会判断相应的 SessionDAO(如继承自CachingSessionDAO)是否实现了CacheManagerAware,如果实现了会把 CacheManager设置给它
    • 设置了缓存的 SessionManager,查询时会先查缓存,如果找不到才查数据库。


    RememberMe


    概述
    • Shiro 提供了记住我(RememberMe)的功能,比如访问如淘宝等一些网站时,关闭了浏览器,下次再打开时还是能记住你是谁,下次访问时无需再登录即可访问,基本流程如下:
    • 1、首先在登录页面选中 RememberMe 然后登录成功;如果是浏览器登录,一般会把 RememberMe 的Cookie 写到客户端并保存下来;
    • 2、关闭浏览器再重新打开;会发现浏览器还是记住你的;
    • 3、访问一般的网页服务器端还是知道你是谁,且能正常访问;
    • 4、但是比如我们访问淘宝时,如果要查看我的订单或进行支付时,此时还是需要再进行身份认证的,以确保当前用户还是你。认证和记住我
    • subject.isAuthenticated() 表示用户进行了身份验证登录的,即使有 Subject.login 进行了登录;
    • subject.isRemembered():表示用户是通过记住我登录的,此时可能并不是真正的你(如你的朋友使用你的电脑,或者你的cookie 被窃取)在访问的
    • 两者二选一,即 subject.isAuthenticated()==true,则subject.isRemembered()==false;反之一样。


    建议


    • 访问一般网页:如个人在主页之类的,我们使用user 拦截器即可,user 拦截器只要用户登录(isRemembered() || isAuthenticated())过即可访问成功;
    • 访问特殊网页:如我的订单,提交订单页面,我们使用authc 拦截器即可,authc 拦截器会判断用户是否是通过Subject.login(isAuthenticated()==true)登录的,如果是才放行,否则会跳转到登录页面叫你重新登录。


    身份验证相关的


    实现


    • 如果要自己做RememeberMe,需要在登录之前这样创建Token:UsernamePasswordToken(用户名,密码,是否记住我),且调用UsernamePasswordToken 的:token.setRememberMe(true); 方法

     

    配置

    展开全文
  • Shiro安全框架入门篇(登录验证实例详解与源码)

    万次阅读 多人点赞 2016-02-03 15:23:32
    一、Shiro框架简单介绍Apache Shiro是Java的一个安全框架,旨在简化身份验证和授权。Shiro在JavaSE和JavaEE项目中都可以使用。它主要用来处理身份认证,授权,企业会话管理和加密等。Shiro的具体功能点如下:(1)...

    一、Shiro框架简单介绍

    Apache Shiro是Java的一个安全框架,旨在简化身份验证和授权。Shiro在JavaSE和JavaEE项目中都可以使用。它主要用来处理身份认证,授权,企业会话管理和加密等。Shiro的具体功能点如下:

    (1)身份认证/登录,验证用户是不是拥有相应的身份;
    (2)授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
    (3)会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;
    (4)加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
    (5)Web支持,可以非常容易的集成到Web环境;
    Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
    (6)shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
    (7)提供测试支持;
    (8)允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
    (9)记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

    文字描述可能并不能让猿友们完全理解具体功能的意思。下面我们以登录验证为例,向猿友们介绍Shiro的使用。至于其他功能点,猿友们用到的时候再去深究其用法也不迟。

    二、Shiro实例详细说明

    本实例环境:eclipse + maven
    本实例采用的主要技术:spring + springmvc + shiro

    2.1、依赖的包

    假设已经配置好了spring和springmvc的情况下,还需要引入shiro以及shiro集成到spring的包,maven依赖如下:

    <!-- Spring 整合Shiro需要的依赖 -->  
    <dependency>  
        <groupId>org.apache.shiro</groupId>  
        <artifactId>shiro-core</artifactId>  
        <version>1.2.1</version>  
    </dependency>  
    <dependency>  
        <groupId>org.apache.shiro</groupId>  
        <artifactId>shiro-web</artifactId>  
        <version>1.2.1</version>  
    </dependency>  
    <dependency>  
        <groupId>org.apache.shiro</groupId>  
        <artifactId>shiro-ehcache</artifactId>  
        <version>1.2.1</version>  
    </dependency>  
    <dependency>  
        <groupId>org.apache.shiro</groupId>  
        <artifactId>shiro-spring</artifactId>  
        <version>1.2.1</version>  
    </dependency>  

    2.2、定义shiro拦截器

    对url进行拦截,如果没有验证成功的需要验证,然后额外给用户赋予角色和权限。

    自定义的拦截器需要继承AuthorizingRealm并实现登录验证和赋予角色权限的两个方法,具体代码如下:

    package com.luo.shiro.realm;
    
    import java.util.HashSet;
    import java.util.Set;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import com.luo.util.DecriptUtil;
    
    public class MyShiroRealm extends AuthorizingRealm {
    
        //这里因为没有调用后台,直接默认只有一个用户("luoguohui","123456")
        private static final String USER_NAME = "luoguohui";  
        private static final String PASSWORD = "123456";  
    
        /* 
         * 授权
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 
            Set<String> roleNames = new HashSet<String>();  
            Set<String> permissions = new HashSet<String>();  
            roleNames.add("administrator");//添加角色
            permissions.add("newPage.jhtml");  //添加权限
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);  
            info.setStringPermissions(permissions);  
            return info;  
        }
    
        /* 
         * 登录验证
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(
                AuthenticationToken authcToken) throws AuthenticationException {
            UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
            if(token.getUsername().equals(USER_NAME)){
                return new SimpleAuthenticationInfo(USER_NAME, DecriptUtil.MD5(PASSWORD), getName());  
            }else{
                throw new AuthenticationException();  
            }
        }
    
    }
    

    2.3、shiro配置文件

    spring-shiro.xml文件内容如下:

    <?xml version="1.0" encoding="UTF-8"?>  
    <beans xmlns="http://www.springframework.org/schema/beans"  
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
        xsi:schemaLocation="http://www.springframework.org/schema/beans   
                            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"  
        default-lazy-init="true">  
    
        <description>Shiro Configuration</description>  
    
        <!-- Shiro's main business-tier object for web-enabled applications -->  
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
            <property name="realm" ref="myShiroRealm" />  
            <property name="cacheManager" ref="cacheManager" />  
        </bean>  
    
        <!-- 項目自定义的Realm -->  
        <bean id="myShiroRealm" class="com.luo.shiro.realm.MyShiroRealm">  
            <property name="cacheManager" ref="cacheManager" />  
        </bean>  
    
        <!-- Shiro Filter -->  
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
            <property name="securityManager" ref="securityManager" />  
            <property name="loginUrl" value="/login.jhtml" />  
            <property name="successUrl" value="/loginsuccess.jhtml" />  
            <property name="unauthorizedUrl" value="/error.jhtml" />  
            <property name="filterChainDefinitions">  
                <value>  
                    /index.jhtml = authc  
                    /login.jhtml = anon
                    /checkLogin.json = anon  
                    /loginsuccess.jhtml = anon  
                    /logout.json = anon  
                    /** = authc  
                </value>  
            </property>  
        </bean>  
    
        <!-- 用户授权信息Cache -->  
        <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />  
    
        <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->  
        <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />  
    
        <!-- AOP式方法级权限检查 -->  
        <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"  
            depends-on="lifecycleBeanPostProcessor">  
            <property name="proxyTargetClass" value="true" />  
        </bean>  
    
        <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">  
            <property name="securityManager" ref="securityManager" />  
        </bean>  
    
    </beans>  

    这里有必要说清楚”shiroFilter” 这个bean里面的各个属性property的含义:

    (1)securityManager:这个属性是必须的,没什么好说的,就这样配置就好。
    (2)loginUrl:没有登录的用户请求需要登录的页面时自动跳转到登录页面,可配置也可不配置。
    (3)successUrl:登录成功默认跳转页面,不配置则跳转至”/”,一般可以不配置,直接通过代码进行处理。
    (4)unauthorizedUrl:没有权限默认跳转的页面。
    (5)filterChainDefinitions,对于过滤器就有必要详细说明一下:

    1)Shiro验证URL时,URL匹配成功便不再继续匹配查找(所以要注意配置文件中的URL顺序,尤其在使用通配符时),故filterChainDefinitions的配置顺序为自上而下,以最上面的为准

    2)当运行一个Web应用程序时,Shiro将会创建一些有用的默认Filter实例,并自动地在[main]项中将它们置为可用自动地可用的默认的Filter实例是被DefaultFilter枚举类定义的,枚举的名称字段就是可供配置的名称

    3)通常可将这些过滤器分为两组:

    anon,authc,authcBasic,user是第一组认证过滤器

    perms,port,rest,roles,ssl是第二组授权过滤器

    注意user和authc不同:当应用开启了rememberMe时,用户下次访问时可以是一个user,但绝不会是authc,因为authc是需要重新认证的
    user表示用户不一定已通过认证,只要曾被Shiro记住过登录状态的用户就可以正常发起请求,比如rememberMe

    说白了,以前的一个用户登录时开启了rememberMe,然后他关闭浏览器,下次再访问时他就是一个user,而不会authc

    4)举几个例子
    /admin=authc,roles[admin] 表示用户必需已通过认证,并拥有admin角色才可以正常发起’/admin’请求
    /edit=authc,perms[admin:edit] 表示用户必需已通过认证,并拥有admin:edit权限才可以正常发起’/edit’请求
    /home=user 表示用户不一定需要已经通过认证,只需要曾经被Shiro记住过登录状态就可以正常发起’/home’请求

    5)各默认过滤器常用如下(注意URL Pattern里用到的是两颗星,这样才能实现任意层次的全匹配)
    /admins/**=anon 无参,表示可匿名使用,可以理解为匿名用户或游客
    /admins/user/**=authc 无参,表示需认证才能使用
    /admins/user/**=authcBasic 无参,表示httpBasic认证
    /admins/user/**=user 无参,表示必须存在用户,当登入操作时不做检查
    /admins/user/**=ssl 无参,表示安全的URL请求,协议为https
    /admins/user/*=perms[user:add:]
    参数可写多个,多参时必须加上引号,且参数之间用逗号分割,如/admins/user/*=perms[“user:add:,user:modify:*”]
    当有多个参数时必须每个参数都通过才算通过,相当于isPermitedAll()方法
    /admins/user/**=port[8081]
    当请求的URL端口不是8081时,跳转到schemal://serverName:8081?queryString
    其中schmal是协议http或https等,serverName是你访问的Host,8081是Port端口,queryString是你访问的URL里的?后面的参数
    /admins/user/**=rest[user]
    根据请求的方法,相当于/admins/user/**=perms[user:method],其中method为post,get,delete等
    /admins/user/**=roles[admin]
    参数可写多个,多个时必须加上引号,且参数之间用逗号分割,如/admins/user/**=roles[“admin,guest”]
    当有多个参数时必须每个参数都通过才算通过,相当于hasAllRoles()方法

    上文参考了http://www.cppblog.com/guojingjia2006/archive/2014/05/14/206956.html,更多详细说明请访问该链接。

    2.4、web.xml配置引入对应的配置文件和过滤器

    <!-- 读取spring和shiro配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:application.xml,classpath:shiro/spring-shiro.xml</param-value>
    </context-param>
    
    <!-- shiro过滤器 -->
    <filter>  
        <filter-name>shiroFilter</filter-name>  
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
        <init-param>  
            <param-name>targetFilterLifecycle</param-name>  
            <param-value>true</param-value>  
        </init-param>  
    </filter>  
    <filter-mapping>  
        <filter-name>shiroFilter</filter-name>  
        <url-pattern>*.jhtml</url-pattern>  
        <url-pattern>*.json</url-pattern>  
    </filter-mapping> 

    2.5、controller代码

    package com.luo.controller;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.subject.Subject;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.servlet.ModelAndView;
    
    import com.alibaba.druid.support.json.JSONUtils;
    import com.luo.errorcode.LuoErrorCode;
    import com.luo.exception.BusinessException;
    import com.luo.util.DecriptUtil;
    
    @Controller
    public class UserController {
    
        @RequestMapping("/index.jhtml")
        public ModelAndView getIndex(HttpServletRequest request) throws Exception {
            ModelAndView mav = new ModelAndView("index");
            return mav;
        }
    
        @RequestMapping("/exceptionForPageJumps.jhtml")
        public ModelAndView exceptionForPageJumps(HttpServletRequest request) throws Exception {
            throw new BusinessException(LuoErrorCode.NULL_OBJ);
        }
    
        @RequestMapping(value="/businessException.json", method=RequestMethod.POST)
        @ResponseBody  
        public String businessException(HttpServletRequest request) {
            throw new BusinessException(LuoErrorCode.NULL_OBJ);
        }
    
        @RequestMapping(value="/otherException.json", method=RequestMethod.POST)
        @ResponseBody  
        public String otherException(HttpServletRequest request) throws Exception {
            throw new Exception();
        }
    
        //跳转到登录页面
        @RequestMapping("/login.jhtml")
        public ModelAndView login() throws Exception {
            ModelAndView mav = new ModelAndView("login");
            return mav;
        }
    
        //跳转到登录成功页面
        @RequestMapping("/loginsuccess.jhtml")
        public ModelAndView loginsuccess() throws Exception {
            ModelAndView mav = new ModelAndView("loginsuccess");
            return mav;
        }
    
        @RequestMapping("/newPage.jhtml")
        public ModelAndView newPage() throws Exception {
            ModelAndView mav = new ModelAndView("newPage");
            return mav;
        }
    
        @RequestMapping("/newPageNotAdd.jhtml")
        public ModelAndView newPageNotAdd() throws Exception {
            ModelAndView mav = new ModelAndView("newPageNotAdd");
            return mav;
        }
    
        /** 
         * 验证用户名和密码 
         * @param String username,String password
         * @return 
         */  
        @RequestMapping(value="/checkLogin.json",method=RequestMethod.POST)  
        @ResponseBody  
        public String checkLogin(String username,String password) {  
            Map<String, Object> result = new HashMap<String, Object>();
            try{
                UsernamePasswordToken token = new UsernamePasswordToken(username, DecriptUtil.MD5(password));  
                Subject currentUser = SecurityUtils.getSubject();  
                if (!currentUser.isAuthenticated()){
                    //使用shiro来验证  
                    token.setRememberMe(true);  
                    currentUser.login(token);//验证角色和权限  
                } 
            }catch(Exception ex){
                throw new BusinessException(LuoErrorCode.LOGIN_VERIFY_FAILURE);
            }
            result.put("success", true);
            return JSONUtils.toJSONString(result);  
        }  
    
        /** 
         * 退出登录
         */  
        @RequestMapping(value="/logout.json",method=RequestMethod.POST)    
        @ResponseBody    
        public String logout() {   
            Map<String, Object> result = new HashMap<String, Object>();
            result.put("success", true);
            Subject currentUser = SecurityUtils.getSubject();       
            currentUser.logout();    
            return JSONUtils.toJSONString(result);
        }  
    }
    

    上面代码,我们只需要更多地关注登录验证和退出登录的代码。
    其中DecriptUtil.MD5(password),对密码进行md5加密解密是我自己写的工具类DecriptUtil,对应MyShiroRealm里面的登录验证里面也有对应对应的方法。
    另外,BusinessException是我自己封装的异常类。
    最后会提供整个工程源码供猿友下载,里面包含了所有的代码。

    2.6、login.jsp代码

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <html>
    <head>
    <script src="<%=request.getContextPath()%>/static/bui/js/jquery-1.8.1.min.js"></script>
    </head>
    <body>
    username: <input type="text" id="username"><br><br>  
    password: <input type="password" id="password"><br><br>
    <button id="loginbtn">登录</button>
    </body>
    <script type="text/javascript">
    $('#loginbtn').click(function() {
        var param = {
            username : $("#username").val(),
            password : $("#password").val()
        };
        $.ajax({ 
            type: "post", 
            url: "<%=request.getContextPath()%>" + "/checkLogin.json", 
            data: param, 
            dataType: "json", 
            success: function(data) { 
                if(data.success == false){
                    alert(data.errorMsg);
                }else{
                    //登录成功
                    window.location.href = "<%=request.getContextPath()%>" +  "/loginsuccess.jhtml";
                }
            },
            error: function(data) { 
                alert("调用失败...."); 
            }
        });
    });
    </script>
    </html>

    2.7、效果演示

    (1)如果未登录前,输入http://localhost:8080/web_exception_project/index.jhtml会自动跳转到http://localhost:8080/web_exception_project/login.jhtml

    (2)如果登录失败和登录成功:

    这里写图片描述

    这里写图片描述

    (3)如果登录成功,访问http://localhost:8080/web_exception_project/index.jhtml就可以到其对应的页面了。

    这里写图片描述

    2.8、源码下载

    http://download.csdn.net/detail/u013142781/9426670

    2.9、我遇到的坑

    在本实例的调试里面遇到一个问题,虽然跟shiro没有关系,但是也跟猿友们分享一下。
    就是ajax请求设置了“contentType : “application/json””,导致controller获取不到username和password这两个参数。
    后面去掉contentType : “application/json”,采用默认的就可以了。

    具体原因可以浏览博文:http://blog.csdn.net/mhmyqn/article/details/25561535

    展开全文
  • Shiro | 实现权限验证完整版

    千次阅读 2018-10-22 03:18:57
    这里只是作为学校Shiro的一个记录,而不是,权限就应该这样设计之类的。 Shiro框架 1、Shiro是基于Apache开源的强大灵活的开源安全框架。 2、Shiro提供了 认证,授权,企业会话管理、安全加密、缓存管理。 3、Shiro...

    写在前面的话

    提及权限,就会想到安全,是一个十分棘手的话题。这里只是作为学校Shiro的一个记录,而不是,权限就应该这样设计之类的。

    Shiro框架

    1、Shiro是基于Apache开源的强大灵活的开源安全框架。

    2、Shiro提供了 认证授权企业会话管理安全加密缓存管理

    3、Shiro与Security对比

    Shiro与Security对比

    4、Shiro整体架构

    Shiro整体架构图

    5、特性

    Shiro特性

    6、认证流程

    Shiro认证流程

    认证

    当我们理解Shiro之后,我们就能比较容易梳理出认证流程,大概就是下面这样子。

    Shiro认证流程

    我们来看一段测试代码:

    // 1.构建SecurityManager环境
    DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
    defaultSecurityManager.setRealm(simpleAccountRealm);
    
    // 2.主体提交认证请求
    SecurityUtils.setSecurityManager(defaultSecurityManager);
    Subject subject = SecurityUtils.getSubject();
    
    // 3.认证
    UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin");
    subject.login(token);
    System.out.println("是否认证:" + subject.isAuthenticated());
    
    // 4.退出
    subject.logout();
    System.out.println("是否认证:" + subject.isAuthenticated());
    

    我们发现Shiro真正帮我们做的就是认证这一步,那他到底是如何去认证的呢?

    Shiro登录过程

    我们把我们登录的代码完善一下:

    /**
     * 登录操作,返回登录认证信息
     * @return 登录结果
     */
    public String login() {
        try {
            DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
            defaultSecurityManager.setRealm(simpleAccountRealm);
    
            SecurityUtils.setSecurityManager(defaultSecurityManager);
            Subject subject = SecurityUtils.getSubject();
    
            UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin");
            
            subject.login(token);
            if (subject.isAuthenticated())
                return  "登录成功";
        } catch (IncorrectCredentialsException e1) {
            e1.printStackTrace();
            return "密码错误";
        } catch (LockedAccountException e2) {
            e2.printStackTrace();
            return "登录失败,该用户已被冻结";
        } catch (AuthenticationException e3) {
            e3.printStackTrace();
            return "该用户不存在";
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "登录失败";
    }
    

    Realm

    1、IniReam

    配置文件 user.ini

    [users]
    Mark=admin,admin
    [roles]
    admin=user:add,user:delete,user:update,user:select
    

    测试代码

    // IniRealm 测试
    @Test
    public void testAuthenticationIniRealm () {
    
        IniRealm iniRealm = new IniRealm("classpath:user.ini");
    
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(iniRealm);
    
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject = SecurityUtils.getSubject();
    
        UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
        subject.login(token);
    
        System.out.println("是否认证:" + subject.isAuthenticated());
    
        subject.checkRole("admin");
        subject.checkPermission("user:delete");
    
    }
    

    2、JdbcRealm

    这里有两种方案,使用Shiro为我们提供了SQL语句,或者我们自己写SQL语句。

    第一种:

    // JdbcRealm 测试 Shiro SQL
    @Test
    public void testAuthenticationShiroSQL() {
    
        JdbcRealm jdbcRealm = new JdbcRealm();
        jdbcRealm.setDataSource(druidDataSource);
        jdbcRealm.setPermissionsLookupEnabled(true);
    
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(jdbcRealm);
    
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject = SecurityUtils.getSubject();
    
        UsernamePasswordToken token = new UsernamePasswordToken("xfsy", "xfsy2018");
        subject.login(token);
    
        System.out.println("是否认证:" + subject.isAuthenticated());
    
        subject.checkRole("user");
        subject.checkPermission("user:select");
    
    }
    

    第二种:

    // JdbcRealm 测试 Custom SQL
    @Test
    public void testAuthenticationCustomSQL() {
    
        JdbcRealm jdbcRealm = new JdbcRealm();
        jdbcRealm.setDataSource(druidDataSource);
        jdbcRealm.setPermissionsLookupEnabled(true);
    
        /**
         * @see org.apache.shiro.realm.jdbc.JdbcRealm
         */
        String sql = "select pwd from t_user where user_name = ?";
        jdbcRealm.setAuthenticationQuery(sql);
    
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(jdbcRealm);
    
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject = SecurityUtils.getSubject();
    
        UsernamePasswordToken token = new UsernamePasswordToken("test", "123");
        subject.login(token);
    
        System.out.println("是否认证:" + subject.isAuthenticated());
    }
    

    3、自定义Realm

    在上面我们已经看过了 JdbcRealm,所以我们也可以依葫芦画瓢自定义Ramlm。

    第一步:继承 AuthorizingRealm

    第二步:实现认证方法

    第三步:实现授权方法

    通过AuthorizingRealm,我们完全按照自己的需求实现自己的业务逻辑。

    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    
    /**
     * @author Wenyi Feng
     * @since 2018-10-22
     */
    public class CustomRealmTest extends AuthorizingRealm {
    
        /**
         * Retrieves the AuthorizationInfo for the given principals from the underlying data store.  When returning
         * an instance from this method, you might want to consider using an instance of
         * {@link org.apache.shiro.authz.SimpleAuthorizationInfo SimpleAuthorizationInfo}, as it is suitable in most cases.
         *
         * @param principals the primary identifying principals of the AuthorizationInfo that should be retrieved.
         * @return the AuthorizationInfo associated with this principals.
         * @see org.apache.shiro.authz.SimpleAuthorizationInfo
         */
        // 授权
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            return null;
        }
    
        /**
         * Returns {@code true} if authentication caching should be utilized based on the specified
         * {@link AuthenticationToken} and/or {@link AuthenticationInfo}, {@code false} otherwise.
         * <p/>
         * The default implementation simply delegates to {@link #isAuthenticationCachingEnabled()}, the general-case
         * authentication caching setting.  Subclasses can override this to turn on or off caching at runtime
         * based on the specific submitted runtime values.
         *
         * @param token the submitted authentication token
         * @param info  the {@code AuthenticationInfo} acquired from data source lookup via
         *              {@link #doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)}
         * @return {@code true} if authentication caching should be utilized based on the specified
         *         {@link AuthenticationToken} and/or {@link AuthenticationInfo}, {@code false} otherwise.
         * @since 1.2
         */
        // 认证
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
                throws AuthenticationException {
            return null;
        }
    }
    

    可能Shiro提供的Realm并不能满足我们的实际开发需求,所以真正弄明白自定义Realm还是有很大帮助的,你觉得呢?

    4、安全加密

    明文密码?

    好吧,我们看看Shiro为我们提供的加密方法。

    //  Md5Hash md5Hash = new Md5Hash("123456");
    
    Md5Hash md5Hash = new Md5Hash("123456", "admin");
    System.out.println(md5Hash.toString());
    

    那我们该怎么使用了?

    在Realm认证中设置盐值

    // 使用admin对密码进行加密
    authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("admin"));
    

    我们只需要告诉我们的Realm,需要对密码进行加密就可以了。

    CustomRealm customRealm = new CustomRealm();
    HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
    // 加密类型
    matcher.setHashAlgorithmName("md5"); 
    // 加密次数
    matcher.setHashIterations(1); 
    customRealm.setCredentialsMatcher(matcher);
    

    权限

    1、Shiro为我们提供的权限

    名称 说明
    anon 不校验
    authc 作校验
    roles 需要具有指定角色(一个或多个)才能访问
    perms 需要具有指定角色(一个或多个)才能访问

    2、配置

    <!-- 从上往下开始匹配 -->
    /login.html = anon
    /subLogin = anon
    <!--/testRole = roles["admin"]-->
    <!--/testRole1 = roles["admin", "admin1"]-->
    <!--/testPerms = perms["user:delete"]-->
    <!--/testPerms1 = perms["user:delete", "user:update"]-->
    

    3、自定义权限认证

    import org.apache.shiro.web.filter.authz.AuthorizationFilter;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    
    /**
     * @author Wenyi Feng
     * @since 2018-10-22
     */
    public class CustomAuthorizationFilter extends AuthorizationFilter {
        
        protected boolean isAccessAllowed(ServletRequest request, 
                                          ServletResponse response, 
                                          Object mappedValue) throws Exception {
            return false;
        }
    }
    

    另外,看一下 Shiro Filter

    Shiro Filter

    会话管理(Session)

    会话管理,就是拿到Session之后,我们怎么处理。什么意思?分布式系统,需要共享Session。

    import org.apache.shiro.session.Session;
    import org.apache.shiro.session.UnknownSessionException;
    import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
    
    import java.io.Serializable;
    import java.util.Collection;
    
    /**
     * 自定义Session操作接口
     * @author Wenyi Feng
     * @since 2018-10-22
     */
    public class CustomSessionDAO extends AbstractSessionDAO {
        
        // 创建Session
        protected Serializable doCreate(Session session) {
            return null;
        }
    
        // 读session
        protected Session doReadSession(Serializable sessionId) {
            return null;
        }
    
        // 修改session
        public void update(Session session) throws UnknownSessionException {
    
        }
    
        // 删除session
        public void delete(Session session) {
    
        }
    
        // 获取当前活动的session
        public Collection<Session> getActiveSessions() {
            return null;
        }
    }
    

    缓存管理(Cache)

    缓存管理同Session管理,可以这样说,session是一套系统的基础,缓存决定系统的优化级别,很重要。

    缓存设计

    另外,我们看一下,Shiro为我们设计的缓存接口。

    package org.apache.shiro.cache;
    
    import java.util.Collection;
    import java.util.Set;
    
    public interface Cache<K, V> {
        V get(K var1) throws CacheException;
    
        V put(K var1, V var2) throws CacheException;
    
        V remove(K var1) throws CacheException;
    
        void clear() throws CacheException;
    
        int size();
    
        Set<K> keys();
    
        Collection<V> values();
    }
    

    自动登录

    我们只需要为token设置RememberMe就可以了。

    token.setRememberMe(user.getRememberMe());
    

    链接

    [1] Shiro安全框架入门

    [2] 本次测试代码:ShiroLearn

    展开全文
  • Shiro入门以及Shiro与web整合

    千次阅读 2019-02-17 18:54:06
    标题Shiro入门以及Shiro与web整合 Shiro框架 - 什么是Shiro? Apache shiro是一个强大,易用的java安全框架执行身份认证,授权,密码和会话管理。 Shiro框架的核心组件 主要核心组件:Subject, Security Manager Realms...
  • Shiro

    2020-11-14 22:27:25
    Shiro 快速开始Shiro 导包 <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.7.0</version> </...
  • shiro

    2020-11-20 10:17:34
    shiro过滤器 需要过滤器控制 没有权限下访问链接 需要标签控制 没有权限下 界面元素的隐藏 anon代表不认证也可以访问,通常对静态资源进行放行 》authc代表必须通过认证才可以访问,通常对动态资源(controller,...
  • Shiro

    万次阅读 多人点赞 2019-04-29 21:12:26
    文章目录一、初识shiro1.shiro简介:2.框架图说明:3.从外部查看shiro框架4.内部结构框架5.常见单词说明6.Shiro中的shiro.ini说明:(1) 、main(2)、users(3)、roles(4)urls6.第一个案例:①项目总体图:添加...
  • shiro

    2020-11-21 10:49:39
    Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can ...
  • shiro、基于url权限管理、超详细

    万次阅读 多人点赞 2018-10-14 15:27:38
    如果需要本篇博客内容的代码!... 项目运行图: 权限管理原理知识   什么是权限管理  只要有用户参与的系统一般都要有权限管理,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问...
  • Spring Boot 中集成 Shiro

    万次阅读 多人点赞 2019-03-08 11:27:38
    Shiro 是一个强大、简单易用的 Java 安全框架,可使认证、授权、加密,会话过程更便捷,并可为应用提供安全保障。本节课重点介绍下 Shiro 的认证和授权功能。 文章目录16.1 Shiro 三大核心组件16.1.1 Subject 为认证...
  • 在这里我使用了Maven作为项目JAR包管理,只要使用下面提供的Shiro在Apache里面的仓库就可以轻松快捷地加入Shiro框架在WEB程序中所需要的代码。 如果没有使用Maven的可以在Shiro官网下载所需要的JAR包也是可以使用的...
  • 在前后端分离的SpringBoot项目中集成Shiro权限框架

    万次阅读 多人点赞 2017-12-12 14:13:47
    项目背景 公司在几年前就采用了前后端分离的开发模式,前端所有请求都使用ajax。这样的项目结构在与CAS单点登录等权限管理框架集成时遇到了很多问题,使得权限部分的代码冗长丑陋,CAS的各种重定向也使得用户体验...
  • GitHub地址:https://github.com/zhang-xiaoxiang/shiro-jwt 说明:由于初衷是解决自己项目的bug的,就找的网上的一面博客瞎搞了一个demo.然后报的错网上难以找到解决办法,后来自己解决了,就记录一下,所以不算教程,我...
  • 如题,希望这篇文章能解答你对 shiro 的一些疑问。
  • Shiro】Apache Shiro架构之实际运用(整合到Spring中)

    万次阅读 多人点赞 2016-07-06 21:05:38
    写在前面:如题,这篇博文主要是总结一下如何将Shiro运用到实际项目中,本来准备将Shiro整到Spring中就行了,后来想想既然要整,就索性把SpringMVC和MyBatis也整进去吧,整个比较完整的,也能帮助更多的初学者。...
  • shiro

    千次阅读 2019-12-29 00:51:06
    shiro 权限管理 权限管理 权限管理分为认证和授权 认证 认证的基本概念 身份认证:校验用户是不是系统合法用户的过程(登录) 身份认证,就是判断一个用户是否为合法用户的处理过程。 最常用的简单身份认证方式是...
  • SpringBoot2.0集成Shiro

    万次阅读 多人点赞 2019-01-28 17:14:08
    最近搞了下shiro安全框架,网上找了好多篇博客,感觉要么都是复制粘贴,要么就是错误百出。至于稍微讲解一下为什么要这么做,就更别说了。这篇文章就教大家如何将 Shiro 整合到 SpringBoot 中,并且避开一些小坑,...
  • Spring Boot Shiro 权限管理

    万次阅读 多人点赞 2016-01-14 23:44:08
    本来是打算接着写关于数据库方面,集成MyBatis的,刚好赶上朋友问到Shiro权限管理,就先总结下发出来了。使用Shiro之前用在Spring MVC中,是通过XML文件进行配置。 既然现在在写Spring Boot的帖子,就将Shiro应用到...

空空如也

1 2 3 4 5 ... 20
收藏数 70,058
精华内容 28,023
关键字:

shiro