美文网首页SpringJava Springshiro
shiro 瞅完就会用(ssm+shiro)

shiro 瞅完就会用(ssm+shiro)

作者: 司鑫 | 来源:发表于2017-04-29 17:43 被阅读3948次
    一 shiro 是什么

    shiro 是一个功能强大和易于使用的Java安全框架,为开发人员提供一个直观而全面的解决方案的认证,授权,加密,会话管理。

    二 shiro 能干什么


    先上图:


    所有功能

    shiro 四个主要的功能

    • Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
    • Authorization:授权,即权限验证,判断某个已经认证过的用户是否拥有某些权限访问某些资源,一般授权会有角色授权和权限授权;
    • SessionManager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的,web 环境中作用是和 HttpSession 是一样的;
    • Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

    shiro 的其它几个特点

    • Web Support:Web支持,可以非常容易的集成到Web环境;
    • Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
    • Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
    • Testing:提供测试支持;
    • Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
    • Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
    三 shiro 架构

    先上图


    架构

    从图中我们可以看到不管是任何请求都会经过 SecurityManager 拦截并进行相应的处理,shiro 几乎所有的功能都是由 SecurityManager 来管理。
    其中:

    • Subject:主体,相当与是请求过来的"用户"
    • SecurityManager: 是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行拦截并控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理
    • Authenticator:认证器,负责主体认证的,即确定用户是否登录成功,我们可以使用  Shiro 提供的方法来认证,有可以自定义去实现,自己判断什么时候算是用户登录成功
    • Authrizer:授权器,即权限授权,给 Subject 分配权限,以此很好的控制用户可访问的资源
    • Realm:一般我们都需要去实现自己的 Realm ,可以有1个或多个 Realm,即当我们进行登录认证时所获取的安全数据来源(帐号/密码)
    • SessionManager:为了可以在不同的环境下使用 session 功能,shiro 实现了自己的 sessionManager ,可以用在非 web 环境下和分布式环境下使用
    • SessionDAO:对 session 的 CURD 操作
    • CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;
    • Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密的。
    四 shiro 的主要功能 - 身份认证

    1 Subject 认证

    身份认证就是在应用中谁能证明他就是他本人,一般会使用用户名和密码作为认证信息。

    2 Subject 认证主体

    Subject 认证主体包含两个信息:

    • Principals:身份,即用户名
    • Credentials:凭证,即密码

    ** 3 认证流程**

    认证流程
    1. 用户发送请求进行 Subject 认证(调用 subject.login(token))
    2. SecurityManager 会去 Authenticator(认证器)中查找相应的 Realms(可能不止一个)源
    3. Realms 可以根据不同类型的 Realm 中去查找用户信息,并进行判断是否认证成功

    4 快速搭建 helloWorld

    1. 导包
    <dependencies>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>1.2.4</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.7.12</version>
            </dependency>
        </dependencies>
    
    1. 创建 Realm /resources/shiro.ini
    [users]
    acey=123456
    jack=111
    
    1. 进行身份验证
    public class HelloWorld {
    
        public static void main(String[] args) {
    //        加载配置文件,初始化 SecurityManager 工厂
            Factory<SecurityManager> factory = new IniSecurityManagerFactory
              ("classpath:shiro.shiro.ini");
    //        获取 SecurityManager 实例
            SecurityManager securityManager = factory.getInstance();
    //        把 SecurityManager 绑定到 SecurityUtils 中
            SecurityUtils.setSecurityManager(securityManager);
    //        得到当前执行的用户
            Subject currentUser = SecurityUtils.getSubject();
    //        创建 token 令牌,用户名/密码
            UsernamePasswordToken token = new UsernamePasswordToken("acey", "123456");
            try {
    //            身份验证
                currentUser.login(token);
                System.out.println("登录成功");
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("登录失败");
            }
        }
    }
    
    
    四 shiro 的主要功能 - 授权

    权限授权就是访问控制,在应用中控制谁能访问哪些资源

    1 权限认证中的几个元素

    • 权限:即操作某个资源的权限,这些资源可以是某个链接,也可以是某个图片,也可以是对某个模块的数据的 CURL
    • 角色:即权限的集合,一个角色可以有多个权限
    • 用户:代表访问的用户,即  Subject

    2 授权的流程

    授权流程
    1. 当用户访问应用中的某个资源时,会被 SecurityManager 拦截.
    2. SecurityManager 会去调用 Authorizer(授权器)
    3. Authorizer 会根据 Subject 的身份去相应的 Realm 中去查找该 Subject 是否有权限去访问该资源

    3 授权实现

    1. 导包
    2. 配置 permission(权限) resources/shiro_permission.ini
    [main] 
    authc.loginUrl=/login  //表示用户登录失败跳转到 /login
    roles.unauthorrizedUrl=/unauthorrized.jsp //表示用户没有对应的访问角色跳转到/unauthorrized.jsp
    perms.unauthorrizedUrl=/unauthorrized.jsp  //表示用户没有对应的访问权限跳转到/unauthorrized.jsp
    
    [users]
    acey=123456,role1,role2
    jack=123,role1
    [roles]
    role1=user:select // role1 角色有访问 user:select 的权限
    role2=user:add,/delete //role2 角色有访问 user:add 和 /delete 的权限
    
    [urls]
    /login=anon  //表示任何用户都可以访问 /login
    /index=authc //表示只有身份认证通过的用户才可以访问 /index
    /index=roles[role1,role2...] //表示只有用户含有 role1 role2 ... 角色才可以访问 /index
    /index=perms["user:create","/update"]  //表示只有用户含有 "user:create" 
                          和"/update"权限才可以访问 /index 
    /index?=authc //`?`通配符,表示一个字符,如/index1 /indexa /index- (不能匹配/index) ,
                          将符合这种规则的请求进行`authc`拦截
    /index*=authc  `*`通配符,表示零个或一个或多个字符,如/index1213asd /index /index2 ,
                          将符合这种规则的请求进行`authc`拦截
    /index/**=authc  `**`表示匹配零个或一个或多个路径,如/index/create /index/create/update/...  ,
                          将符合这种规则的请求进行`authc`拦截
    /index*/**authc  可以匹配 /index12/create/update/...
    
    

    3)配置 roles (角色) resources/shiro_role.ini

    [users]
    acey=123456,role1,role2 //表示有一个用户,用户名是acey,密码为123456,有role1和role2角色
    jack=123,role1
    
    1. 验证用户角色是否足够
    public class RoleTest {
    
    //  使用 checkRole 来检验角色时,若权限不足会返回 false
        @Test
        public void testHasRole() {
            Subject currentUser= ShiroUtil.login("classpath:shiro_role.ini", "acey", "123456");
            // Subject currentUser=ShiroUtil.login("classpath:shiro_role.ini", "jack", "123");
            System.out.println(currentUser.hasRole("role1")?"has role1":"has not role1");
            currentUser.logout();
        }
    
        //  使用 checkRole 来检验角色时,若权限不足会抛出异常
        @Test
        public void testCheckRole() {
            Subject currentUser=ShiroUtil.login("classpath:shiro_role.ini", "acey", "123456");
            // Subject currentUser=ShiroUtil.login("classpath:shiro_role.ini", "jack", "123");
            currentUser.checkRole("role1");
    
            currentUser.logout();
        }
    }
    
    1. 验证用户权限是否足够
    public class PermissionTest {
    
    //  使用 checkPermission 来检验权限时,若权限不足会返回 false
        @Test
        public void testIsPermitted() {
            Subject currentUser= ShiroUtil.login("classpath:shiro_permission.ini", "acey", "123456");
            System.out.println(currentUser.isPermitted("user:select")?"has user:select":"hsa not user:select");
    
            currentUser.logout();
        }
    
    //  使用 checkPermission 来检验权限时,若权限不足会抛出异常
        @Test
        public void testCheckPermitted() {
            Subject currentUser=ShiroUtil.login("classpath:shiro_permission.ini", "acey", "123456");
            // Subject currentUser=ShiroUtil.login("classpath:shiro_permission.ini", "jack", "123");
            currentUser.checkPermission("user:select");
            currentUser.logout();
        }
    }
    
    五 ssm 和 shiro 整合

    1. 导入依赖
      2)配置 web.xml(shiro过滤器)
     <!-- shiro过滤器定义 -->
        <filter>  
            <filter-name>shiroFilter</filter-name>  
            <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
        <init-param>  
        <!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 -->  
        <param-name>targetFilterLifecycle</param-name>  
        <param-value>true</param-value>  
        </init-param>  
        </filter>  
        <filter-mapping>  
                <filter-name>shiroFilter</filter-name>  
                <url-pattern>/*</url-pattern>  
        </filter-mapping>
        
        
    

    3)编写自己的 Realm(一般权限都是从数据库中查找,所以需要自定义)

    public class MyRealm extends AuthorizingRealm{
    
        @Resource
        private UserService userService;
        
        /**
         * 为当前登录的用户授予角色和权限
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            //获取用户名
            String userName=(String)principals.getPrimaryPrincipal();
            SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
            //进行授权角色
            authorizationInfo.setRoles(userService.getRoles(userName));
            //进行授权权限
            authorizationInfo.setStringPermissions(userService.getPermissions(userName));
            return authorizationInfo;
        }
    
        /**
         *验证当前登录的用户
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            String userName=(String)token.getPrincipal();
            //根据用户名查找用户信息
                User user=userService.getByUserName(userName);
                if(user!=null){
                    AuthenticationInfo authcInfo=new SimpleAuthenticationInfo(user.getUserName(),user.getPassword(),getName());
                    return authcInfo;
                }else{
                    return null;                
                }
        }
    }
    
    1. spring 和 shiro 配置整合
    ...
    <!-- 自定义Realm -->
        <bean id="myRealm" class="com.acey.realm.MyRealm"/>
        
        <!-- 安全管理器 -->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
          <property name="realm" ref="myRealm"/>  
        </bean>  
        
        <!-- Shiro过滤器 -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
            <!-- Shiro的核心安全接口,这个属性是必须的 -->  
            <property name="securityManager" ref="securityManager"/>
            <!-- 身份认证失败,则跳转到登录页面的配置 -->  
            <property name="loginUrl" value="/index.jsp"/>
            <!-- 权限认证失败,则跳转到指定页面 -->  
            <property name="unauthorizedUrl" value="/unauthor.jsp"/>  
            <!-- Shiro连接约束配置,即过滤链的定义 -->  
            <property name="filterChainDefinitions">  
                <value>  
                     /login=anon
                    /admin*=authc
                    /student=roles[teacher]
                    /teacher=perms["user:create"]
                </value>  
            </property>
        </bean>  
        
        <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->  
        <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>  
        
        <!-- 开启Shiro注解 -->
        <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>  
            <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">  
          <property name="securityManager" ref="securityManager"/>  
        </bean>  
    ...
    

    一般角色和权限都存在数据库中,所以我们还可以自定义一个 filter 去自己验证每一个请求的 Subject 是否有权限去访问,这样我们就可以减少对过滤链的维护.比如

    <!-- Shiro过滤器 -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
            <!-- Shiro的核心安全接口,这个属性是必须的 -->  
            <property name="securityManager" ref="securityManager"/>
            <!-- 身份认证失败,则跳转到登录页面的配置 -->  
            <property name="loginUrl" value="/index.jsp"/>
            <!-- 权限认证失败,则跳转到指定页面 -->  
            <property name="unauthorizedUrl" value="/unauthor.jsp"/>  
            <!-- Shiro连接约束配置,即过滤链的定义 -->  
            <property name="filterChainDefinitions">  
                <value>  
                     /login=anon
                    /admin*=authc
                    /student=roles[teacher]
                    /teacher=perms["user:create"]
                </value>  
            </property>
        </bean>  
    
    

    可以改成

    <!-- Shiro过滤器 -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
            <!-- Shiro的核心安全接口,这个属性是必须的 -->  
            <property name="securityManager" ref="securityManager"/>
            <!-- 身份认证失败,则跳转到登录页面的配置 -->  
            <property name="loginUrl" value="/index.jsp"/>
            <!-- 权限认证失败,则跳转到指定页面 -->  
            <property name="unauthorizedUrl" value="/unauthor.jsp"/>  
            <property name="ownFilter" class="ownFilter.class">
            <!-- Shiro连接约束配置,即过滤链的定义 -->  
            <property name="filterChainDefinitions">  
                <value>  
                     /login=anon
                   /**=ownFilter
                </value>  
            </property>
        </bean>  
    

    待续! 欢迎大家拍砖

    源码地址:ShirDemos

    相关文章

      网友评论

      • bc9b09cde488:SysUser sysUser=(SysUser) SecurityUtils.getSubject().getPrincipal();这句强转错误请问遇到过吗?
      • rangel:这个评论的人真厉害,竟然这么认真😂😂
        司鑫:@rangel 怎么都谈到梦想了:joy:
        rangel: @Acey 厉害咯,那你也赶紧好好努力,实现你的梦想哈😊
        司鑫:@rangel TW的大牛哦:grin:
      • rangel:虽然我没有用过,但是感觉你总结的很认真,很好😁
        司鑫:@rangel 你一定没有看下面的评论:wink:问题挺多的。主要还是基础不扎实。。。
      • 张羽辰:看完了整个 demo,看来你对于 shiro 的接口理解了部分,首先呢 shiro_1 shiro_2 shiro_3 这三个 demo 完全重复,只是使用的身份库不同,完全可以放在一起,在 shiro_3 中测试完全写崩了,建议去看看单元测试、功能测试。

        shiro_4 是做了一个 Spring 的集成,首先不要使用 SSM 这种不好的、无意义的缩写,shiro 是可以支持配置的身份库的,那用不用 MyBaits 就没什么区别了;UserService wrap 了一层 DAO,无意义,反倒是可以讲 challenge 的过程放到这个 service 中,让 controller 更清晰。controller 中不需要直接输出 system.out ,可以使用 java.util.log,抓异常的方式太差劲,另外,真的需要 serlevt 的 request 吗?我看不需要的,既然已经有框架,为何需要底层的 object 呢?

        UserMapper.xml 中 SQL 语句需要优化,特别是 getPermissions,另外 role,permission 都需要建模为 entity。

        这个例子中完全不需要加入 MVC ,反倒会误导别人。你要做的是写一个简单的 service 就可以说明这个东西是可以和 spring 一起工作就可以了。我觉得只需要第四个 demo 都够了,其他的都无意义。建议使用 spring-boot,让你需要些更少的代码(一个 controller,一个 service 就足够了)。

        最后一个 demo 是最需要 unit test 的,但是木有……

        小细节要注意,indent、package name、method signature 、comments 等等……






        司鑫:@张羽辰 好的:fist:
        张羽辰:@Acey :up: 可以看看 github 中 https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples 这些例子是怎么写的,先模仿模仿
        司鑫:@张羽辰 好的,明天重新改一遍:fist:

      本文标题:shiro 瞅完就会用(ssm+shiro)

      本文链接:https://www.haomeiwen.com/subject/ismzzttx.html