志宇-shiro-web

Shiro的使用场景

在单机应用集群部署环境时可以使用Shiro,因Shiro提供了相关持久化接口,底层写好了将session持久化到redis数据库
在微服务中不推荐使用shiro,因每个模块都要权限校验
Auth2.0: 用于微信端授权登录
jwt: 用于sessionid的加密和解密技术

Shiro的核心类

SecurityUtils

用于 获得 SecurityManager 和 Subject 对象
将这两个对象从 ThreadLocal中获取,或存放

Subject

这个对象主要用于一些权限校验比如 hasRole、isAuthenticated、isRemembered、login、logout方法
Subject维护着两个对象一个是SecurityManager,一个是Session
Subject校验方法调用SecurityManager对象中的方法
Subject的Session对象维护着SessionManager对象

DefaultWebSecurityManager

这个类中有很多成员变量,它可以调用每个成员变量的主要方法,我们只要给这个类设置对应的成员变量即可, 主要成员变量如下,

  1. Realm 实现 认证 和 授权两个方法(realm中也维护了很多对象)
  2. SubjectDAO 用于将Subject中的主要信息持久化
  3. SubjectContext 用于创建Subject
  4. SessionManager 会话管理
  5. CacheManager 认证和授权缓存的管理
  6. Authorizer 授权对象
  7. Authenticator 认证对象

类图如下

在这里插入图片描述

类图中每个类的作用

DefaultSecurityManager: 用来管理 RememberMeManager、SubjectDAO、SubjectFactory、SubjectContext 对象

SessionsSecurityManager: 用来管理sessionManager对象

AuthorizingSecurityManager: 用来管理Authorizer对象

AuthenticatingSecurityManager: 用来管理Authenticator对象

RealmSecurityManager: 用来管理Realm 对象

CachingSecurityManager: 用来管理CacheManager、EventBus对象

主要方法

这个类主要使用的方法: login、logout、remember方法
login 主要流程

info = this.authenticate(token);
---》info = this.doAuthenticate(token);  
    //获得所有绑定的Realm,先上缓存找,如果缓存没开或者没找到则调用Relam的doGetAuthenticationInfo方法
--------this.assertCredentialsMatch(token, info);
        //查看是token是否匹配,调用Realm中CredentialsMatcher对象的doCredentialsMatch方法
---this.notifySuccess(token, info);
    //这里是一个监听,调用AuthenticationListener对象的onSuccess方法
this.onFailedLogin(token, ae, subject);
//登录失败调用这个方法 
---》rmm.onFailedLogin(subject, token, ex);
    //给客户端设置一个登录失败的Cookie信息 response.addHeader("Set-Cookie", headerValue);
Subject loggedIn = this.createSubject(token, info, subject);
---》SubjectContext context = this.createSubjectContext();
    //创建DefaultSubjectContext对象
---this.createSubject(context);
--------》context = this.ensureSecurityManager(context);
--------------》context.setSecurityManager(this);
               //把SecurityManager设置给DefaultSubjectContext
--------》context = this.resolveSession(context);
              //从MapContext的Map中获得一个值,有值则通过
--------》context = this.resolvePrincipals(context);
             //将Principals设置给DefaultSubjectContext  context.setPrincipals(principals);
--------》Subject subject = this.doCreateSubject(context);
            //调用SubjectFactory接口实现类,创建Subject 返回一个WebDelegatingSubject对象
--------this.save(subject);
         //调用SubjectDAO接口实现类save方法存储 Subject对象中的主要信息   
--------------if (this.isSessionStorageEnabled(subject)) {this.saveToSession(subject);
                //获得SessionStorageEvaluator接口实现类 调用isSessionStorageEnabled方法
               //当情况允许的情况下 
----------------------this.mergePrincipals(subject);
                       //将 principals 通过反射存放 DelegatingSubject字段中
                       //将principals 放到session中
                       //通过sessionDao 将信息持久化到数据库或内存
----------------------this.mergeAuthenticationState(subject);
                     //如果授权成功在sesison中放一个属性,属性值为true
                     //通过sessionDao 将信息持久化到数据库或内存
 this.onSuccessfulLogin(token, info, loggedIn);
     //调用RememberMeManager 中的方法 rmm.onSuccessfulLogin(subject, token, info);

logout 主要流程

this.beforeLogout(subject);
    //调用RememberMeManager 的方法  rmm.onLogout(subject);
    //刷新Cookie
((LogoutAware)authc).onLogout(principals);
    //循环调用所有的AuthenticationListener执行onLogout方法
 this.delete(subject);
    //subjectDAO.delete(subject);  删除对应的session
    //session.removeAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);
    //session.removeAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
 this.stopSession(subject);
---》Session s = subject.getSession(false);
    //获得 DelegatingSubject(Subject)中的Session对象DelegatingSession
---》s.stop();
--------this.sessionManager.stop(this.key);
--------------》Session session = this.lookupRequiredSession(key);
 --------------》session.stop();
--------------this.onStop(session, key);
--------------this.notifyStop(session);
--------------this.afterStopped(session);

Shiro的拦截器主要类

DefaultFilter

shiro总共定义了11种拦截器,我只要配置这些拦截器会拦截那些路径即可,当我们有特殊需求时也可以自己自定义的拦截器进行拦截

public enum DefaultFilter {
    //匿名拦截器,不需要登录即可访问的资源,匿名用户或游客,一般用于过滤静态资源
    anon(AnonymousFilter.class),
    //需要认证登录才能访问
    authc(FormAuthenticationFilter.class),
    //httpBasic 身份验证拦截器
    authcBasic(BasicHttpAuthenticationFilter.class),
    //退出拦截器
    logout(LogoutFilter.class),
    noSessionCreation(NoSessionCreationFilter.class),
    //权限授权拦截器,验证用户是否拥有权限
    perms(PermissionsAuthorizationFilter.class),
    //配置哪些路径  要固定端口才能访问
    port(PortFilter.class),
    rest(HttpMethodPermissionFilter.class),
    //配置哪些路径 要哪些角色才能访问
    roles(RolesAuthorizationFilter.class),
    //配置哪些路径 要https 访问
    ssl(SslFilter.class),
    //用户拦截器,表示必须存在用户
    user(UserFilter.class);
 }

shiroFilterFactoryBean

shiroFilterFactoryBean实现了Spring中的FactoryBean、BeanPostProcessor
实现FactoryBean接口说明,它可以给spring创建对象通过getObject()方法
实现BeanPostProcessor接口,说明它是spring的一个后置处理器
项目启动时,后置处理器会在属性赋值后,也就是spring每个对象初始化(spring的init方法)前后执行
这个类的主要作用就是获得配置的自定义Filter和默认的Filter以及要拦截url对象创建放到spring中
在 getObject方法

FilterChainManager manager = this.createFilterChainManager();
--》DefaultFilterChainManager manager = new DefaultFilterChainManager();
  //在DefaultFilterChainManager 创建一个Filter集合 和一个Filter链,
  //将DefaultFilter枚举中的所有Filter放到集合中
--》Map<String, Filter> filters = this.getFilters();
--》manager.addFilter(name, filter, false)) 
  //将自己定义的Filter放到集合中
--》manager.createChain(url, chainDefinition);
  //将Filter和url分别放到对应的容器中

在spring后置处理器的前置方法中

if (bean instanceof Filter) {
this.applyGlobalPropertiesIfNecessary(filter);
this.getFilters().put(beanName, filter);
}

对所有Filter 进行拦截,包装下,然后将Filter放到shiroFilterFactoryBean中

一个自带的拦截器类图

在这里插入图片描述
NameableFilter:
用来给这个Filter设置一个name
OncePerRequestFilter:
一个人不能在同时间执行两次这个方法,就好比上了一把锁,方法执行时候把锁打开,执行完打开锁
,这里更新了sessionid的失效时间,同时执行后面的filter
AdviceFilter
在执行chain.doFilter(request, response);前后调用
boolean continueChain = this.preHandle(request, response);
//返回boolean类型,如果返回false则不继续执行
this.postHandle(request, response); 交给子类去实现

Shiro的Filter配置路径说明

路径通配符支持 ?、*、**,注意通配符匹配不 包括目录分隔符“/”
匹配说明
?  : 匹配一个字符,如 /user? , 匹配 /user3,但不匹配/user/;
*  : 匹配零个或多个字符串,如 /add* ,匹配 /addtest,但不匹配 /user/1
** : 匹配路径中的零个或多个路径,如 /user/** 将匹 配 /user/xxx 或 /user/xxx/yyy
例子
/user/**=filter1
/user/add=filter2

Shiro配置Filter路径

1、配置的信息要放到LinkedHashMap,否则 部分路径无法进行拦截,时有时无
2、要按顺序放到拦截器中,权限从小到大依次放入
3、LinkedHashMap中第一个参数是 拦截路径,第二个是拦截器名称(名字在枚举类DefaultFilter中)


 //拦截器路径,坑一,部分路径无法进行拦截,时有时无;因为同学使用的是hashmap, 无序的,应该改为
 LinkedHashMap Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

//退出过滤器
filterChainDefinitionMap.put("/logout","logout");

//匿名可以访问,也是就游客模式
filterChainDefinitionMap.put("/pub/**","anon");

//登录用户才可以访问
filterChainDefinitionMap.put("/authc/**","authc");

//管理员角色才可以访问
filterChainDefinitionMap.put("/admin/**","roleOrFilter[admin,root]");

//有编辑权限才可以访问
filterChainDefinitionMap.put("/video/update","perms[video_update]");

//坑二: 过滤链是顺序执行,从上而下

//authc : url定义必须通过认证才可以访问
//anon  : url可以匿名访问
filterChainDefinitionMap.put("/**", "authc");

shiro注解注解使用

不推荐使用注解,springboot中要配置一些配置类,注解才能生效

@RequiresRoles(value={"admin", "editor"}, logical= Logical.AND)
需要角色 admin 和 editor两个角色 AND表示两个同时成立

@RequiresPermissions (value={"user:add", "user:del"}, logical= Logical.OR)
需要权限 user:add 或 user:del权限其中一个,OR是或的意思。

@RequiresAuthentication
已经授过权,调用Subject.isAuthenticated()返回true

@RequiresUser
身份验证或者通过记 住我登录的

使用下面代码替换

subject.hasRole("xxx");
subject.isPermitted("xxx");
subject. isPermittedAll("xxxxx","yyyy");
subject.checkRole("xxx"); // 无返回值,可以认为内部使用断言的方式

shiro缓存使用

可以将shiro的认证和授权所得到的资源缓存起来
缓存后再次使用资源就不会走Realm中的认证和授权方法了

类图如下

在这里插入图片描述
在这里插入图片描述

CachingRealm

这个Realm主要起管理缓存对象,同时可以设置是否开启缓存
因实现了CacheManagerAware接口,实现了setCacheManager方法,要在子类中注入CacheManager对象,所以CachingRealm封装了CacheManager 类型的对象

AuthenticatingRealm

这个Relam是在认证时起到缓存作用, AuthenticatingRealm在构造方法中设置了

this.authenticationCachingEnabled = false;

代表默认 认证不会开启缓存(因为不会频繁登录,用户修改了密码还要改缓存)

AuthorizingRelam

这个Relam是在授权时起到缓存作用, AuthorizingRelam在构造方法中设置了

this.authorizationCachingEnabled = true;

代表默认 授权会开启缓存 (相对来说访问较多,修改权限次数较少)

CacheManager

//它实现了CacheManager 同时是抽象的 可以通过继承他重写缓存主要方法
//它实现了InitializingBean  会在spring将对象发到ioc容器中后 调用(一般用于ioc容器对象中的成员变量赋值)
public abstract class AbstractCacheManager implements CacheManager, InitializingBean {)

CachingRealm中有用到CacheManager类型对象,用这个对象来指定如何缓存
CacheManager是一个接口,抽象类AbstractCacheManager实现了 CacheManager
如果自己写不建议使用AbstractCacheManager类作为父类,因为它会将缓存信息放到Map中
直接实现CacheManager接口即可,想缓存到redis中要导入如下依赖

   <!-- shiro+redis缓存插件 -->
        <dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>3.1.0</version>
        </dependency>

会提供一个RedisCacheManager对象,它实现了CacheManager接口
RedisCacheManager中有一个成员变量 redisManager
redisManager用来连接redis数据库
RedisCacheManager用来从redis中获取数据 (不能往redis中设置数据,只能取)
要想往Redis中设置数据要创建 redisSessionDao对象
同时redisSessionDao对象要通过DefaultWebSessionManager 对象进行绑定

会话管理

SessionManager
start 开启一个session
getSession 通过sessionid获得session
在这里插入图片描述
DefaultWebSessionManager是SessionManager 类型的
这个对象里面维护着 cacheManager 和 SessionDAO
使用redis缓存sessionid的话 就将RedisSessionDAO对象设置给SessionDAO接口
SessionDAO接口负责将sessionid缓存到指定数据库
如何自定义sessionid key的格式和value
key要在 DefaultWebSessionManager 中进行修改
value 要在redisSessionDAO 的SessionIdGenerator 类修改

密码加密使用

盐值加密
加密时候加点盐就好了 (这个盐可以是注册时候 创建的放到用户表中)

校验密码

首先要获得输入的密码 和从数据库加密的密码
我们要将输入的密码加密 然后和数据库中的密码比对
如果我们想要自己定义方法,同时解密时候也要使用
那么shiro要在哪里进行校验密码呢?
当然是认证环节了,也就是 AuthenticatingRealm 类了
AuthenticatingRealm中有一个对象CredentialsMatcher 这个就是用来来加密的对象
HashedCredentialsMatcher参数使用
在这里插入图片描述
同时 在认证时传入参数中要传入盐
在这里插入图片描述

加密

在注册密码和修改密码时进行加密
在这里插入图片描述

在这里插入图片描述

自学项目地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值