springboot shiro安全框架使用

很长的前言(老司机忽略):

1.去百度/官方文档了解Subject、SecurityManager、Realm这些shiro的概念

2.分清authentication(认证)、authorization(授权)这两个单词

3.了解cookie和session

4.需要了解:数据库、spring的各层术语、jpa、role(角色)与permission(权限)

5.建议前面说的多少了解些,不然后面太多东西集成,一大串不懂心很烦
下面跟着这个走:github上的一个例子
再推荐一个教程:教你 Shiro 整合 SpringBoot,避开各种坑
我这里是自底向上,根据已有的东西往上建设。


先完成登陆认证:

1.把DAO层、service层、jpa的Entity这些弄好,数据库自己弄好,能连上没问题后进入正题

2.写一个MyShiroRealm类继承AuthorizingRealm类。
有两个方法需要重写:doGetAuthorizationInfo(权限授权相关,这里跳过)。
AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)(重点是这个),我们需要从这里完成从数据库中获取对应账户和密码的工作,这样后面就不用再管数据库了。

(1)参数token可以看作UsernamePasswordToken(AuthenticationToken 的子类),一般我们后面都会传入这个。token.getPrincipal()方法将返回我们“账户密码”中的“账户”,在例子中这个是一个代表用户名的字符串。根据这个去查询数据库,把该用户的信息取出来用。

(2)返回一个SimpleAuthenticationInfo的对象。用该对象封装必要的登陆信息(这样就屏蔽了我们userinfo对象的差异)。四参数构造函数中,第一个参数代表用户,随便设置,后面SecurityUtils.getSubject().getPrincipal()就返回这个,自己看需要;二、三参数分别代表加密后的密码和加密用的盐;第四个参数是如果后面我们需要指定哪个Realm来获取认证信息时才用到的。

3.Configuration中先写好3个必要的@Bean
myShiroRealm、securityManager、hashedCredentialsMatcher这三个。

(1)hashedCredentialsMatcher就是密码匹配器。它用于把密码加密后与密文对应,这个东西需要交给Realm使用。代码中myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());这里把密码匹配器交给Realm

(2)securityManager。了解过相关概念的话,登陆认证调用其实是SecurityUtils--->securityManager--->Realm的doGetAuthenticationInfo,因此在securityManager需要把我们的Realm交给它,代码中securityManager.setRealm(myShiroRealm());这里就是把Realm交给securityManager(可用setRealms(Collection<Realm> realms)方法来完成多reaml认证)。
至于securityManager自己,如果没有写Filter,则这里应当有一句SecurityUtils.setSecurityManager(securityManager);来把其交给SecurityUtils。但例子中没有这行代码,是由于Filter中已经设置了,当Filter被注入后,SecurityUtils就自动获得了该securityManager

4.Configuration中其他的Bean先放下(注意第三点说的,如果没有写Filter,自行找个地方把securityManager交给SecurityUtils),上面的完成后,我们即可在web层Controller中操作了,先注入两个参数username和password,后面就可以

UsernamePasswordToken token=new UsernamePasswordToken(username,password);
Subject subject= SecurityUtils.getSubject();
subject.login(token);

这样就完成最基本登陆认证了。有了这个基础去找其他文章,看他们的例子补充rememberMe、session、验证码实现的相关知识。
这里贴一份实现rememberMe功能的:(rememberMe记住我功能)


授权相关

1.回头写MyShiroRealm类的doGetAuthorizationInfo

(1)其参数PrincipalCollection principals代表一个身份集合。这个集合主要是由于可能存在多个Realm,比如你有两张表,一张常规用户表一张用户黑名单表,一个用户可能在两个表里都有,这样你就会写两个继承Realm的类,它们会放进两个不同的user(即上面2.2点中说到的第一个参数)。它的方法getPrimaryPrincipal()就是返回主要的user对象(自行强制转换),如果只有一个Realm知道这些就足够了。如果有多个Realm,则用fromRealm(String realmName)即可,其参数realmName对应上面2.2点中说的第四个参数。详情右转api文档

(2)返回一个SimpleAuthorizationInfo对象。你需要把这个用户拥有的角色和权限以字符串的形式封装进这个对象:addRole(String role)addStringPermission(String permission)使用这两个方法(有重载方法参数直接为一个集合)。

(3)视乎你的代码设计,这里的代码差异较大。例子中的user对象可以直接获取role/permission对应的字符串,如果你user对象不是这个储存结构,可以在这里想办法再去查询一遍数据库来获取角色/权限数据,再封装进SimpleAuthorizationInfo对象。

2.关于这个Permission,它其实是shiro的一个接口,只不过为了使用方便,99.9%情况下都会以字符串的形式来表示一个Permission对象,shiro有能力把我们的字符串换成Permission对象,我们只管操作字符串表示Permission就行
关于Permission字符串的格式、权限和角色的使用:Shiro入门9

3.subject.hasRole("xxx")subject.checkPermission("xxx")这些方法就是根据你doGetAuthorizationInfo返回的AuthorizationInfo对象来判断的

4..如果想在web层的Controller中使用shiro提供的角色/权限控制注解,参考例子中Configuration的authorizationAttributeSourceAdvisor这个Bean,就几行。写完这个Bean就可以开启shiro的注解了,当然注解的role/permission的判断同第3点

5.如果只使用上面3、4点的方法来进行授权管理,那么当规模很大时,你维护起来就要到处找,不直观不方便。因此这里还有最后的一个shirFilter这个Bean(spring自动注入到shiro,写了这个bean就行),你可以在这里进行集中管理。

(1)看代码看注释,不难理解大部分的东西

(2)Map<String,String> filterChainDefinitionMap,用来配置拦截器链,其中key为url,value为对应的拦截器类(怎么对应看下面给的链接),不同的拦截器类有不同的拦截效果

举个非常简单的例子:
在这里写filterChainDefinitionMap.put("/hello", "perms[userInfo:del]");和你在对应Controller上写@RequiresPermissions("userInfo:del")注解是一样的效果;filterChainDefinitionMap.put("/hello","roles[admin]"@RequiresRoles("admin")是一样的效果,其他看代码注释不难理解。
更多详细右转:Shiro几大拦截器


总结和坑

1.Subject的isRemembered()方法实现为:

    public boolean isRemembered() {
        PrincipalCollection principals = this.getPrincipals();
        return principals != null && !principals.isEmpty() && !this.isAuthenticated();
    }

看这里“!this.isAuthenticated()”,也就是说,如果当前用户这次浏览经过了账户密码认证,那么isRemembered一定返回false。也就是说“已认证”会覆盖“记住我”

2.chrome等浏览器中直接删除rememberMe这个cookie,F5刷新后cookie仍然生效。
shiro缓存有毒,即使此时浏览器不再发送cookie,此时isRemember仍然为true,摸不清这个缓存机制。此时重启浏览器/重启服务端即可。

3.shiroFilterFactoryBean.setSuccessUrl("/index");无效的问题

(1)shiro过滤链跟servlet的过滤链可看作一样,shiro把自己的过滤链放在servlet的所有Filter之前而已。

(2)shiroFilterFactoryBean.setLoginUrl("/login");作用是无权访问"authc"的页面时强制跳转到这里

(3)shiroFilterFactoryBean.setSuccessUrl("/index");当且仅当:login页面的form提交方法为post且拦截包括"/login"时,一旦登陆成功就会跳转。

这个东西比较鸡肋,第一次看例子时一直看不懂Controller中"/login"的登陆逻辑,就是这个B。实际流程是shiro拦截访问并重定向到"login",在处理方法返回值,shiro会从过滤链里获取request,再得到username、password、rememberMe这几个提交的值,由shiro帮你组织并调用subject.login来登陆,再进入处理方法,因此例子中login的处理方法主要就是处理有无错误/异常,最后由Filter更改其跳转,也就是此时会返回setSuccessUrl设置的url,而非控制器中处理方法的值。

而一旦我们设置了访问"/login"页面可以匿名访问时,那么登陆逻辑全部由我们自己来做,shiro不会做任何成功跳转之类的行为,也就是该情况下setSuccessUrl无效。

总结来说:"/login"受到shiro拦截时,可以偷懒不写登陆逻辑,控制器中对应的处理方法没什么用,并且成功登陆后跳转到setSuccessUrl设置的值。"/login"不受拦截可匿名访问时,需要在处理方法内自己完成登陆逻辑,此时setSuccessUrl不生效,符合常规控制器处理方法的mvc模式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容

  • 说明:本文很多观点和内容来自互联网以及各种资料,如果侵犯了您的权益,请及时联系我,我会删除相关内容。 权限管理 基...
    寇寇寇先森阅读 7,593评论 8 76
  • doGetAuthorizationInfo和doGetAuthenticationInfo 这两个方法虽然名字很...
    Kenny_Yu阅读 1,453评论 1 1
  • ꧂启红#好的我也不知道考试前一晚瞎写了啥#大概是启红# 村子里有个很厉害的人。 从我记事起,就看到他了,大家都叫他...
    萧唯shawee阅读 2,604评论 1 3
  • 他的心里有一个王国 我踏进 无以伦比的青色草地 在曼舞 在永不停息的欢愉 不如归去
    薛笙阅读 245评论 0 0
  • 深夜花睡去, 醒来思故人。 说好的放下和忘记, 不过是自我慰籍。 清晰的身影, 熟悉的气息, 记忆的片断, 早已融...
    无界小玉阅读 204评论 0 1