springboot 中使用 shiro 大都是通过 shiro-spring.jar 进行的整合的, 虽然不是太复杂,但是也无法做到 spring-boot-starter 风格的开箱即用。项目中经常用到的功能比如:验证码、密码错误次数限制、账号唯一用户登陆、动态 URL 过滤规则、无状态鉴权等等,shiro 还没有直接提供支持。
jsets-shiro-spring-boot-starter 对这些常用的功能进行了封装和自动导入,少量的配置就可以应用在项目中。
1、spring-boot-starter 风格的开箱即用。
2、区分 ajax 请求和普通请求,普通请求通过跳转来响应未登陆和未授权,AJAX 请求通过状态码和消息响应未登陆和未授权。
3、集成 jcaptcha 验证码。
4、密码输入错误,重试次数限制。
5、账号唯一用户登陆,一个账号只允许一个用户登陆。
6、redis 缓存支持,认证 \ 授权数据缓存同步。
7、动态 URL 过滤规则。
8、无状态认证授权支持,共存有状态和无状态两种鉴权方式,无状态鉴权支持 JWT(JSON web TOKEN)、HMAC(哈希消息认证码) 两种协议。
9、在线 session 管理,强制用户下线功能。
1、cas 单点登陆集成
2、oauth2.0 支持
此文介绍应用 jsets-shiro-spring-boot-starter 来为一个 springboot 应用提供鉴权支持,限于篇幅,没有贴出源码只贴出了演示效果,您可以在文末 "项目文档、源码" 中获取全部源码。
1、在 application.properties 添加下面配置:
- #是否启用验证码验证,不配置默认不启用
- jsets.shiro.jcaptcha-enable=true
- #是否启用账号唯一用户登陆(一个账号只允许一个用户登陆,后面登陆的用户踢出前面登陆的用户),不配置默认不启用
- jsets.shiro.keep-one-enabled=true
- #是否启用强制用户下线(系统管理员通过session操作迫使指定用户下线),不配置默认不启用
- jsets.shiro.force-logout-enable=true
- #登陆页面
- jsets.shiro.login-url=/login
- #登陆成功页面
- jsets.shiro.login-success-url=/index
- #无权限调转页面
- jsets.shiro.unauthorized-url=/unauthorized
- #用户被踢出后跳转的页面,keep-one-enabled=true时此项有用,不配置默认login-url
- jsets.shiro.kickout-url=/kickout
- #用户被强制下线后跳转的页面,force-logout-enable=true时此项有用,不配置默认login-url
- jsets.shiro.force-logout-url=/force_logout
- #密码错误最大次数,不配置默认无限次
- jsets.shiro.passwd-max-retries=5
- #基本静态过滤规则
- jsets.shiro.filte-rules[0]=/assets/**-->anon
- jsets.shiro.filte-rules[1]=/login/**-->authc
- jsets.shiro.filte-rules[2]=/logout/**-->logout
- jsets.shiro.filte-rules[3]=/**-->user,keepOne,forceLogout
2、实现 Controller 类 IndexAction,主要用来做各种跳转。
3、实现登陆界面 login.html
4、实现主页 index.html
如果此时启动 springboot 应用,在控制台会打印出组件预置的体验账号和密码:
控制台体验账号
使用这个账号和密码就可以登陆系统了。
验证码为空的情况
账号或密码错误的情况
登陆成功情况
如果您使用其他浏览器或者使用其他的电脑用这个账号登陆,当前登陆的用户会被后来登陆的用户踢出。
当前用户被踢出
上面跑起来的应用,使用的是组件中提供的体验账号和密码,实际项目中肯定要接入自己的用户数据,角色数据、权限数据。
1、实现 ShiroAccountProvider 接口 ShiroAccountProviderImpl,作用是为组件提供鉴权数据。
2、实现 PasswdRetryLimitHandler 接口 ShiroFilteRulesProviderImpl,作用是响应密码输入错误次数超限时的回调。
3、实现 DemoJsetsShiroConfiguration 继承自 JsetsShiroConfigurationAdapter,用来设置 ShiroAccountProviderImpl 实现和 ShiroFilteRulesProviderImpl。
如果此时重启系统,将不再使用体验账号,而是使用您自己的账号数据进行鉴权。
当用户密码输入错误到达限制次数后将会锁定用户。
重试错误提示
错误次数超限锁定
大部分系统的安全模型都是 RBAC(Role-Based Access Control 基于角色的权限访问控制),使用用户 -- 角色 -- 资源 (URL) 的对应关系来表示并存储。
能够访问某个资源可以看成是一种权限,资源 (URL) 就是这个权限的表示,角色就是权限的集合。访问一个资源 (URL) 时候都会检查当前用户是否拥有对应的角色。
假如有资源,文章删除 / article/delete、文章编辑 / article/update、文章发布 / article/publish。编辑角色 (role_editor) 拥有编辑的权限,主编角色 (role_chief) 拥有删除和发布的权限,这样的 URL 过滤规则在 shiro 中表示为:
- article/update=roles[role_editor]
- article/delete=roles[role_chief]
- article/publish=roles[role_chief]
如果系统中如果有几十个上百个资源 (URL),需要这样细粒度的权限控制,静态配置显然是困难的。好在 shiro 中提供了基于 AOP 的方式可以进行这样的分散控制,类似于这样:
- @RequiresRoles("role_editor")
- public void update(Article article){
- }
但是如果角色 - 资源的对应关系发生变更,或者添加了角色,那就只能修改配置或者代码了,还得要重新启动系统才能使得这些变更生效。
实际开发中则更希望通过 URL 进行集中的权限控制,能够从数据库中查询出角色 - 资源的对应数据,生成 URL 过滤规则,当角色 - 资源的对应关系发生变更时能够刷新这些 URL 过滤规则。
在 jsets-shiro-spring-boot-starter 中通过 ShiroFilteRulesProvider 为组件提供动态过滤规则。
1、实现 ShiroFilteRulesProvider 接口 ShiroFilteRulesProviderImpl
2、在 DemoJsetsShiroConfiguration 的中设置 ShiroFilteRulesProviderImpl。
从角色列表中能看到角色 "role_admin" 没有文章编辑的权限。
角色列表
进入 "文章管理" 界面,点击 "编辑" 按钮,会发现因权限不足不能操作。
权限不足
重新为角色 "role_admin" 分配资源,添加 "修改文章"。
资源分配
再次点击 "编辑" 文章就可以操作了。
角色验证成功
当然,实际项目中是不会让用户看到不具操作权限的菜单或按钮的。
无状态 (Stateless) 鉴权通常应用在微服务 (REST API) 架构中,使用数字摘要 (签名) 技术生成一个 token 作为认证和授权的凭证,整个认证和授权过程不依赖于 cookie 或 session,服务端不保留客户端状态因此每次请求都要携带这个 token。
jsets-shiro-spring-boot-starter 提供两种无状态鉴权方式,分别是散列消息认证码 (HMAC)、JSON WEB TOKEN(JWT)。
HMAC 适合端到端的鉴权,即客户端拿着签名让服务端进行验签。
JWT 适合客户端询问系统 B 是否有权访问系统 C 和系统 N 如果有请开份凭证,然后拿着凭证让系统 C 和系统 N 进行验签。
1、在 application.properties 添加 HMAC 或者 JWT 的相关配置
- #是否启用HMAC鉴权,不配置默认不启用
- jsets.shiro.hmac-enabled=true
- #HMAC签名算法,不配置默认HmacMD5,hmac-enabled=true时此项有用
- #jsets.shiro.hmac-alg=HmacMD5
- #HMAC签名全局秘钥,hmac-enabled=true时此项有用
- jsets.shiro.hmac-secret-key=ofaffadfev1234567--090swctewst
- #HMAC签名有效期,不配置默认1分钟,hmac-enabled=true时此项有用
- #jsets.shiro.hmac-period=60000
- #是否启用JWT鉴权,不配置默认不启用
- jsets.shiro.jwt-enabled=true
- #JWT签名签名全局秘钥,jwt-enabled=true时此项有用
- jsets.shiro.jwt-secret-key=ofaffadfev1234567--090swctewst
2、配置 URL 拦截规则,示例项目中使用的是动态配置。
- #匹配此路径的URL,需要通过hmac认证并具有role_admin角色
- jsets.shiro.filte-rules[3]=/restApi/delete*-->hmacRoles[role_admin]
- #匹配此路径的URL,需要通过hmac认证
- jsets.shiro.filte-rules[4]=/restApi/**-->hmac
- #匹配此路径的URL,需要通过jwt认证并具有role_admin角色
- jsets.shiro.filte-rules[5]=/restApi2/delete*-->jwtRoles[role_admin]
- #匹配此路径的URL,需要通过jwt认证
- jsets.shiro.filte-rules[6]=/restApi2/**-->jwt
访问这些 api 会使用 HMAC 或者 JWT 进行鉴权。
hmac 验签成功
当超过签名的有效期后,再次用这个签名进行鉴权,签名将过期失效。
签名过期
生成签名之后,手动在签名后面加了一个字符 "3",说明请求内容和签名不符,即请求发生了篡改,签名无效。
签名无效
HMAC 和 JWT 也具有权限验证的能力,我们用 guest 账号登陆,点击 "请求 delete"。
权限不足
不生成令牌直接点击请求,因为没有令牌会要求进行身份认证。
11.png
展示所有活跃的用户 (session),系统管理员可以强制这些用户下线。
当前在线用户
当前用户被管理员强制下线
1、jsets-shiro-spring-boot-starter 项目详情请参见: jsets-shiro-spring-boot-starter
2、应用示例源码请参见: jsets-shiro-demo
3、jsets-shiro-spring-boot-starter 使用说明请参见: 使用说明
来源: http://www.jianshu.com/p/40d11d18ead6