shiro框架的学习总结

昨天刚把shiro框架学完,算是比较系统的学习吧,怕过几天忘了细节,所以就先记录一下。

基于web应用的登录认证和授权管理问题

据了解(加上个人理解),不使用安全框架,一般情况下,处理安全问题的数据库模型是RBAC(role base access controller),就是由角色控制可以访问的资源,然后给用户分配角色,这样就使得角色有了某种权限,权限就是各种controller的请求路径。这种做法就需要把所有的请求路径都管理起来,如果controller层的请求路径有变动,数据库就要变动,使得管理不方便。如果用面向对象的开放封闭原则来说,这算是一种差的做法。
然后再说一下shiro框架,就用了新的RBAC的思想,resources base access controller,基于资源的控制,把资源分类,然后管理起来,这样就又出现了粗粒度和细粒度权限这个区别。比如一个product类,从资源的角度看,粗粒度的权限就是能查看所有product,就是product:list,细粒度的权限就是能对某一id的product查看详情,product:show:[id]。
然后再加上方法级的权限控制和前端菜单显示控制,就能达到天衣无缝的权限控制。

springSecurity框架

Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.
Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements
它是一个强大的高度客制化的认证和访问控制的框架,它符合spring应用的标准。
它是一个聚焦于Java应用的认证和授权的框架,像所有的spring项目一样,它致力于能够轻松的满足客制化的需求。
使用框架的前提还是要很好的配置,感觉配置的好才能很好的发挥框架的作用。之前用过,是基于springboot的应用,但现在忘完了,一般是它配合Spring Security使用,所以只在这里提一下。

shiro框架

shiro框架是今天的主角。它同样非常强大。
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序百度

主要功能

三个核心组件:Subject, SecurityManager 和 Realms.
Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。
Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果系统默认的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。
在这里插入图片描述

shiro框架具体使用

jar包的maven坐标
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-web -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>1.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>
shiro过滤器配置

在web.xml配置文件中加入shiro filter

<!--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>/*</url-pattern>
</filter-mapping>

这里实质是配置servlet filter的代理类,用DelegatingFilterProxy的好处主要是通过Spring容器来管理servlet filter的生命周期。还有就是如果filter中需要一些Spring容器的实例,可以通过spring直接注入,另外读取一些配置文件这些便利的操作都可以通过Spring来配置实现。详见Spring:代理Filter:DelegatingFilterProxy原理和作用
用了代理类,就要注意开启使用代理类<!--开启代理类--> <aop:config proxy-target-class="true"/> 的配置

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.xsd">
<!--shiro核心组件-->
    <bean class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" id="securityManager">
        <property name="realm" ref="customRealm"/>
        <property name="cacheManager" ref="ehcacheManager"/>
        <property name="sessionManager" ref="sessionManager"/>
        <property name="rememberMeManager" ref="rememberMeManager"/>
    </bean>
<!--    自定义realm-->
    <bean class="com.example.realm.CustomRealm" id="customRealm">
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </bean>
<!--密码适配-->
    <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher" id="credentialsMatcher">
        <property name="hashAlgorithmName" value="md5"/>
        <property name="hashIterations" value="2"/>
    </bean>
<!--缓存管理器-->
    <bean class="org.apache.shiro.cache.MemoryConstrainedCacheManager" id="ehcacheManager"></bean>
<!--    session会话管理器-->
    <bean class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager" id="sessionManager">
        <property name="deleteInvalidSessions" value="true"/>
    </bean>
<!--    rememberMeManager管理器,写cookie,读cookie生成用户信息-->
    <bean class="org.apache.shiro.web.mgt.CookieRememberMeManager" id="rememberMeManager">
        <property name="cookie" ref="cookie"/>
    </bean>
    <bean class="org.apache.shiro.web.servlet.SimpleCookie" id="cookie">
<!--        cookie的名字-->
        <constructor-arg value="rememberMe"/>
<!--        最大存活时间-->
        <property name="maxAge" value="12960000"/>
    </bean>
<!--shiro的拦截规则,拦截链-->
    <bean class="org.apache.shiro.spring.web.ShiroFilterFactoryBean" id="shiroFilter">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/login"/>
        <property name="unauthorizedUrl" value="/pages/refuse.jsp"/>
        <property name="filterChainDefinitions">
            <value>
<!--                退出,清除session-->
                /logout=logout
<!--                授权后才能访问,使用方法级权限注解代替-->
<!--                /product/findAll=perms[product:query]-->
<!--                使用rememberMe之后就能访问-->
                /test=user
                /main=user
                <!--认证后才能访问-->
                /**=authc
                <!--匿名访问-->
<!--                /**=anon-->
            </value>
        </property>
    </bean>
</beans>
自定义realm实现

自定义realm的实现,将决定如何实现登录认证和授权管理。重写的doGetAuthorizationInfodoGetAuthenticationInfo 方法被授权器和认证器调用。

package com.example.realm;
import com.example.domain.Permission;
import com.example.domain.UserInfo;
import com.example.service.UserInfoService;
import org.apache.shiro.authc.*;
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 org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/**
 * @className CustomRealm
 * @date 2021/4/15 9:58
 * @description 自定义shiro的realm,实现登录认证和授权管理
 **/
public class CustomRealm extends AuthorizingRealm {
    @Autowired
    private UserInfoService userInfoService;
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("CustomRealm.doGetAuthorizationInfo");
        Object principal = principalCollection.getPrimaryPrincipal();
        UserInfo userInfo = userInfoService.findUserByName((String) principal);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        List<Permission> permissions = userInfoService.findPermissionByUid(userInfo.getId());
        for(Permission permission : permissions){
            info.addStringPermission(permission.getUrl());
        }
        return info;
    }
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("CustomRealm.doGetAuthenticationInfo");
        Object principal = authenticationToken.getPrincipal();
        UserInfo userInfo = userInfoService.findUserByName((String) principal);
        if(null == userInfo)
            throw new UnknownAccountException("用户未知");
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(userInfo.getUsername(), userInfo.getPassword(), ByteSource.Util.bytes(userInfo.getSalt()),getName());
        return info;
    }
}

既然是用spring框架,依赖注入就不说了。
先说一下doGetAuthenticationInfo 方法,用于认证的过程实现。大概流程就是根据获取的身份principal 从数据库查找该身份的密码credentials ,然后与在登录页面输入的密码进行对比,如果一样就通过认证,如果不一样就拒绝认证。
再说一下doGetAuthorizationInfo 方法,用于授权管理过程。在用户判断是否有某种权限时,调用此方法从数据库获取该principal 的权限,然后与要判断的权限做比较,返回布尔值。

认证具体流程

1、Shiro把用户的数据封装成标识token,token一般封装着用户名,密码等信息,用户登录时调用subject.login(token);
2、Subject把标识token交给SecurityManager,这里用的DefaultSecurityManager,
3、在SecurityManager安全中心中,SecurityManager把标识token委托给认证器AbstractAuthenticator,认证器获取到本次认证用的realm,
4、然后调用ModularRealmAuthenticator的doAuthenticate方法,它拿着自定义的realm和token去做认证。

授权具体流程

1、对subject进行授权,调用isPermitted(xxx),
2、securityManager执行授权,通过ModularRealmAuthorizer进行授权,
3、ModularRealmAuthorizer调用realm(自定义realm)的doGetAuthorizationInfo方法从数据库获取当前身份principal的权限返回给ModularRealmAuthorizer,
4、ModularRealmAuthorizer调用isPermitted进行权限串比较,返回Boolean值。

方法级权限和角色
@RequestMapping("/findAll")
    @RequiresPermissions("product:query")//基于注解授权判断
    @RequiresRoles("normal")//基于注解角色判断
    public ModelAndView findAll(){
        ModelAndView mv = new ModelAndView();

        List<Product> productList = productService.findAll();

        mv.addObject("productList",productList);
        mv.setViewName("product-list");
        return mv;
    }

个人感觉这是基于资源访问控制的最有效,最简便的,最直接的方式,这种方式固定了访问资源需要的权限,把问题留给了对访问者的角色或权限的分配控制,感觉达到了对整个系统权限管理的最好境界。

jsp页面权限控制的使用
<!--在jsp上加上这段内容-->
<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro" %>
<td>
<!-- 有item:update权限才显示修改链接,没有该 权限不显示,相当 于if(hasPermission(item:update)) -->
<shiro:hasPermission name="item:update">
<a href="${pageContext.request.contextPath }/items/editItems.action?id=${item.id}">修改</a>
</shiro:hasPermission>
</td>

shiro的缓存

由于有了权限控制,这样就使得在访问有权限限制的资源时,就要调用realm从数据库读取该用户的权限,导致频繁的进行数据库操作,影响系统性能,所以就对用户的权限进行缓存。

使用缓存的配置

在spring-shiro.xml中配置缓存管理器,然后引入到securityManager中。这里使用shiro自带的缓存管理器。

<!--缓存管理器-->
<bean class="org.apache.shiro.cache.MemoryConstrainedCacheManager" id="ehcacheManager"></bean>

通过debug测试,缓存确实有效。

shiro的rememberMe功能
配置
<!--    rememberMeManager管理器,写cookie,读cookie生成用户信息-->
<bean class="org.apache.shiro.web.mgt.CookieRememberMeManager" id="rememberMeManager">
    <property name="cookie" ref="cookie"/>
</bean>
<bean class="org.apache.shiro.web.servlet.SimpleCookie" id="cookie">
<!--        cookie的名字-->
    <constructor-arg value="rememberMe"/>
<!--        最大存活时间-->
    <property name="maxAge" value="12960000"/>
</bean>
<!--使用rememberMe之后就能访问-->
/test=user
/main=user

这个功能利用浏览器的cookie实现。

源码浅析

在这里插入图片描述
如果不配置cookie的名字,源码就自动设为rememberMe,而且在rememberMeManager需要注入一个cookie,所以就在容器中注入cookie来使用。
在这里插入图片描述

写在最后

总结大概就是这样了,只是浅薄的理解和记录。希望我能多翻一翻,看一看自己的总结,对答如流。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
学习Shiro框架可以按照以下步骤进行: 1. 了解基础概念:首先,你需要了解Shiro的基本概念和术语,例如主体(Subject)、认证(Authentication)、授权(Authorization)、Realm等。可以阅读Shiro的官方文档或者相关的教程来获得这些知识。 2. 安装和配置:安装Shiro框架并进行基本的配置。你可以在Shiro的官方网站上找到安装指南和配置示例。 3. 认证功能:学习如何使用Shiro进行用户认证,包括用户名密码认证、Remember Me功能、多Realm认证等。可以尝试编写简单的认证示例来理解这些功能。 4. 授权功能:学习如何使用Shiro进行用户授权,包括角色授权和权限授权。了解如何定义角色和权限,并且如何在代码中进行授权判断。 5. Session管理:了解Shiro如何管理用户的会话信息,包括会话超时、会话验证等。学习如何使用Shiro提供的Session API来管理会话。 6. 整合框架:如果你使用其他的Java框架,例如Spring或者Spring Boot,学习如何将Shiro与这些框架进行整合,以便更好地利用Shiro的功能。 7. 安全性优化:深入了解Shiro安全性能优化技巧,例如密码加密、安全配置、防止常见安全漏洞等。 8. 实战练习:通过编写实际的应用程序来巩固所学的知识。可以尝试开发一个简单的Web应用程序,使用Shiro进行用户认证和授权。 除了官方文档和教程,还可以参考一些优秀的书籍或在线教程,例如《Apache Shiro官方指南》、《深入浅出Shiro安全框架》等。此外,加入Shiro的社区或者论坛,与其他开发者交流经验也是一个很好的学习方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值