美文网首页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