为什么要用shiro:
1.项目中的密码是否可以明文存储?
2.是否任意访客,无论是否登录都可以访问任何功能?
3.项目中的各种功能操作,是否是所有用户都可以随意使用?
综上,当项目中的某些功能被使用时,需要进行安全校验,进而保证整个系统的运行秩序。
• Apache Shiro 是 Java 的一个安全(权限)框架。
Shiro 可以轻松的完成:身份认证、授权、加密、会话管理等功能
• Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE 环境,也可以用在 JavaEE 环境。
功能强大且易用,可以快速轻松地保护任何应用程序 ( 从最小的移动应用程序到最大的Web和企业应用程序。)
• 方便的与Web 集成和搭建缓存。
• 下载:http://shiro.apache.org/
• Authentication
身份认证/登录,验证用户是不是拥有相应的身份;
• Authorization
授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能进行什么操作
如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
• Session Manager
会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有
信息都在会话中;会话可以是普通 JavaSE 环境,也可以是 Web 环境的;
• Cryptography
加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
• Web Support
Web 支持,可以非常容易的集成到Web 环境;
• Caching
缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
• Remember Me
记住我,这个是非常常见的功能,即一次登录后,下次再来的话可以立即知道你是哪个用户
。。。。
shiro 运行流程中,3个核心的组件:
Subject、SecutiryManager、Realm
shiro使用中,必须有的3个概念!!
• Subject
安全校验中,最常见的问题是"当前用户是谁?" "当前用户是否有权做x事?",所以考虑安全校验过程最自
然的方式就是基于当前用户。Subject 代表了当前“用户”,
应用代码直接交互的对象是 Subject,只要得到Subject对象马上可以做绝大多数的shiro操作。
也就是说 Shiro 的对外API 核心就是 Subject。
Subject 会将所有交互都会委托给 SecurityManager。
==Subject是安全管理中直接操作的对象==
• SecurityManager
安全管理器;即所有与安全有关的操作都会与SecurityManager 交互;
且其管理着所有 Subject;它是 Shiro的核心,
它负责与 Shiro 的其他组件进行交互,它相当于 SpringMVC 中DispatcherServlet 的角色
• Realm
Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说SecurityManager 要验证用户身份,那么
它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;
也需要从 Realm 得到用户相应的角色/权限进行验证用户是否能进行操作;
可以把 Realm 看成 DAO,( 数据访问入口 )
Role Base Access Controll 基于角色的访问控制
shiro采用的安全管理 模型
模型中3个主体:用户、角色、权限
每个角色可以有多个权限,每个权限可以分配给多个角色
每个用户可以有多个角色,每个角色可以分配给多个用户
两个多对多
则权限访问控制,做的事是:
- 身份校验:判断是否为合法用户
- 权限校验:用户要做做某件事或使用某些资源,必须要拥有某角色,或必须拥有某权限
在访问控制管理过程中,是要对项目中的资源(功能,数据,页面元素等)的访问、使用进行安全管理。
1> 首先照旧记录用户信息
2> 然后制定角色
如
“千锋学员”,“千锋讲师”,“保洁员”
3> 然后会对"资源"制定权限:即能对资源做的所有操作
如
"教室"
-资源,"进入教室"
,“在教室学习”
,就是该资源的两个权限。4> 然后将权限分配给不同角色
如
“进入教室”
分配给“千锋学员”,“千锋讲师”,“保洁员”
三种角色
“在教室学习”
分配给“千锋学员”
角色5> 最后将角色分配给具体用户
如
“张三”
报名后分配“千锋学员”
角色
“李四”
入职千锋分配"千锋保洁员"
角色6> 如此完成对 用户(李四,张三)使用某资源的访问控制
则:张三能不能进教室?李四能不能在教室学习?
简单了解,在对shiro有了完整的应用体验后可以重点了解!
• Subject
任何可以与应用交互的“用户”;
• ###SecurityManager
相当于SpringMVC 中的 DispatcherServlet;是 Shiro 的心脏;
所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证、授权、会话及缓存的管理。
• Authenticator
负责 Subject 身份认证,是一个扩展点,可以自定义实现;可以使用认证
策略(Authentication Strategy),即什么情况下算用户认证通过了;
• Authorizer
授权器、即访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
• Realm
可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体
的;可以是JDBC 实现,也可以是内存实现等等;由用户提供;所以一般在应用中都需要
实现自己的 Realm;
• SessionManager
管理 Session 生命周期的组件;而 Shiro 并不仅仅可以用在 Web环境,也可以用在如普通的 JavaSE 环境
• CacheManager
缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少改变,
放到缓存中后可以提高访问的性能
• Cryptography
密码模块,Shiro 提供了一些常见的加密组件用于如密码加密/解密。
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-coreartifactId>
<version>1.4.0version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>1.7.12version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.7.12version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.16version>
dependency>
<dependency>
<groupId>commons-logginggroupId>
<artifactId>commons-loggingartifactId>
<version>1.2version>
dependency>
ops : require JDK 1.6+ and Maven 3.0.3+
创建一个
shiro.ini
文件
#定义用户信息
#格式:用户名=密码,角色1,角色2,....
[users]
zhangsan=123,admin
lisi=456,manager,seller
wangwu=789,clerk
# -----------------------------------------------------------------------------
# 角色及其权限信息
# 预定权限:user:query
# user:detail:query
# user:update
# user:delete
# user:insert
# order:update
# ....
[roles]
# admin 拥有所有权限,用*表示
admin=*
# clerk 只有查询权限
clerk=user:query,user:detail:query
# manager 有 user 的所有权限
manager=user:*
- 通过在ini中设置的 身份信息、角色、权限,做安全管理
- 1.身份认证: subject.login(token)
- 2.是否认证身份判断:subject.isAuthenticated()
- 3.角色校验:subject.hasRole(String:role); subject.hasRoles(List:roles)
- 4.权限校验:subject.isPermitted(String:perm); subject.isPermittedAll(List:perms)
// 定义main函数测试效果
// 创建 "SecurityFactory",加载ini配置,并通过它创建SecurityManager
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
// 将SecurityManager托管到SecurityUtils工具类中(ops:之后可以不必关心SecurityManager)
SecurityUtils.setSecurityManager(securityManager);
// 获得Subject,通过subject可以执行shiro的相关功能操作(身份认证或权限校验等)
Subject currentUser = SecurityUtils.getSubject();
// 身份认证( 类似登录逻辑 )
if (!currentUser.isAuthenticated()) {//判断是否已经登录
//如果未登录,则封装一个token,其中包含 用户名和密码
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
try {
//将token传入login方法,进行身份认证 (判断用户名和密码是否正确)
currentUser.login(token);//如果失败则会抛出异常
} catch (UnknownAccountException uae) {//用户不存在
System.out.println("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {//密码错误
System.out.println("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {//账户冻结
System.out.println("The account for username " + token.getPrincipal() + " is locked. "
+"Please contact your administrator to unlock it.");
}catch (AuthenticationException ae) {//其他认证异常
}
}
// 认证成功则用户信息会存入 currentUser(Subject)
System.out.println("User [" + currentUser.getPrincipal() + "] logged in successfully.");
// 可以进一步进行角色校验 和 权限校验
if (currentUser.hasRole("admin")) { //校验角色
System.out.println("hello,boss");
} else {
System.out.println("Hello, you");
}
if (currentUser.isPermitted("user:update")) { //校验权限
System.out.println("you can update user");
} else {
System.out.println("Sorry, you can not update.");
}
// 用户退出,会清除用户状态
currentUser.logout();
// System.exit(0);
##3.4 权限规则
最常用的权限标识:【资源 :操作】
1> user:query , user:insert , order:delete , menu:show
【:】 作为分隔符,分隔资源和操作【资源:操作】
【,】 作为分隔,分隔多个权限【权限1,权限2,权限3】
2> user:* , *:query
【*】 作为通配符,代表所有操作、资源
【user:* 即user的所有操作】
【*:query 即所有资源的查询操作】
3> *
【代表一切资源的一切权限 = 最高权限】
4> 细节:
1)【user:* 可以匹配 user:xx, user:xx:xxx】
【*:query 只可以匹配 xx:query,不能匹配 xx:xx:query. 除非 *:*:query】
2)【user:update,user:insert】 可以简写为 【"user:update,insert"】
[roles]
manager1=user:query,user:update,user:insert
manager2="user:query,update,insert" #注意要加引号
#如上manager1和manager2权限等价
#subject.isPermittedAll("user:update","user:insert","user:query")
实例级权限标识:【资源 :操作 :实例】,粒度细化到具体某个资源实例
1> user:update:1 , user:delete:1
# 对用户1可以update,对用户1可以delete
2> "user:update,delete:1" #和上面等价
# subject.isPermittedAll("user:update,delete:1","user:update:1","user:delete:1")
3> user:*:1 , user:update:* , user:*:*
与web项目集成后,shiro的工作模式如下:
如上:ShiroFilter拦截所有请求,对于请求做访问控制
如请求对应的功能是否需要有 认证的身份,是否需要某种角色,是否需要某种权限。
1> 如果没有做 身份认证,则将请求强制跳转到登录页面。
如果没有充分的角色或权限,则将请求跳转到权限不足的页面。
2> 如果校验成功,则执行请求的业务逻辑
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>jsp-apiartifactId>
<version>2.1version>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>jstlartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>4.3.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-coreartifactId>
<version>1.4.0version>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-webartifactId>
<version>1.4.0version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>1.7.12version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.7.12version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.16version>
dependency>
定义一些Servlet 或 springMVC的controller,发送请求,验证shiro的验证
@Controller
@RequestMapping("/user")
public class ShiroController {
@RequestMapping("/delete")
public String deleteUser(){//访问此删除功能时要先经过shiro的安全校验
System.out.println("delete User");
return "forward:/xx.jsp";
}
@RequestMapping("/update")
public String updateUser(){//访问此更新功能时要先经过shiro的安全校验
System.out.println("update User");
return "forward:/xx.jsp";
}
@RequestMapping("/insert")
public String insertUser(){//访问此增加功能时要先经过shiro的安全校验
System.out.println("insert User");
return "forward:/xx.jsp";
}
@RequestMapping("/login/page")
public String login(String username,String password){
System.out.println("goto login.jsp");
return "forward:/login.jsp";
}
@RequestMapping("/login/logic")
public String login(String username,String password){//登录功能不能被shiro校验,否则永不能登录
try{
Subject subject = SecurityUtils.getSubject();
subject.login(new UsernamePasswordToken(username,password));
String uname = (String)subject.getPrincipal();
System.out.println("uname:"+uname);
}catch (AuthenticationException e){
e.printStackTrace();
return "redirect:/login.jsp";
}
return "forward:/success.jsp";
}
}
springMVC的相关配置照旧。
安装
ShiroFilter!!!!
<filter>
<filter-name>shiroFilterfilter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilterfilter-class>
filter>
<filter-mapping>
<filter-name>shiroFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListenerlistener-class>
listener>
新增两个部分(section), [main] 和 [urls]
[users]
zhangsan=123,admin
lisi=456,manager,seller
wangwu=789,clerk
[roles]
admin=*
clerk=user:query,user:detail:query
manager=user:*
[main]
#没有身份认证时,跳转地址
shiro.loginUrl = /user/login/page
#角色或权限校验不通过时,跳转地址
shiro.unauthorizedUrl=/author/error
#登出后的跳转地址,回首页
shiro.redirectUrl=/
[urls]
# 如下格式:"访问路径 = 过滤器"
#【1.ant路径:? * ** 细节如下】
# /user/login/page , /user/login/logic 是普通路径
# /user/* 代表/user后还有一级任意路径 : /user/a , /user/b , /user/c , /user/xxxxxxxxxxx
# /user/** 代表/user后还有任意多级任意路径: /user/a , /user/a/b/c , /user/xxxx/xxxxxx/xxxxx
# /user/hello? 代表hello后还有一个任意字符: /user/helloa , /user/hellob , /user/hellox
#【2.过滤器,细节如下】
# anon => 不需要身份认证
# authc => 指定路径的访问,会验证是否已经认证身份,如果没有则会强制转发到 最上面配置的loginUrl上
# ( ops:登录逻辑本身千万不要被认证拦截,否则无法登录 )
# logout => 访问指定的路径,可以登出,不用定义handler。
# roles["manager","seller"] => 指定路径的访问需要subject有这两个角色
# perms["user:update","user:delete"] => 指定路径的访问需要subject有这两个权限
/user/login/page = anon
/user/login/logic = anon
/user/query = authc
/user/update = authc,roles["manager","seller"]
/user/delete = authc, perms["user:update","user:delete"]
/user/logout = logout
#其余路径都需要身份认证【用此路径需谨慎】
/** = authc
#【3.注意】
# url的匹配,是从上到下匹配,一旦找到可以匹配的则停止,所以,通配范围大的url要往后放,
# 如"/user/delete" 和 "/user/**"
定义 Controller中和 shiro.ini中需要的jsp
通过ShiroFilter和定义在shiro.ini中的配置信息,即可在项目接收用户访问时,进行身份,角色,权限进行访问控制啦!!!!
shiro提供了很多标签,用于在jsp中做安全校验。
完成对页面元素的访问控制
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>
....
**
** **已登录 已登录或记住我 游客(未登录 且 未记住我) **
**
** 未登录 获取用户身份信息
<shiro:authenticated>
欢迎您,<shiro:principal/>
shiro:authenticated>
<shiro:user>
欢迎您,<shiro:principal/>
shiro:user>
<shiro:guest>
欢迎您,未登录,请<c:url value="/user/login/page"/>">登录a>
shiro:guest>
<shiro:notAuthenticated>
您尚未登录(记住我也算在未登录中)
shiro:notAuthenticated>
是其中任何一种角色 是指定角色
不是指定角色
<table>
<tr>
<td>idtd>
<td>nametd>
<td>operationtd>
tr>
<tr>
<td>001td>
<td>张三td>
<td>
<shiro:hasAnyRoles name="admin,manager">
<a href="#" style="text-decoration:none">详情a>
shiro:hasAnyRoles>
<shiro:hasRole name="admin">
<a href="#" style="text-decoration: none">删除a>
shiro:hasRole>
<shiro:lacksRole name="admin">
<a href="#" style="text-decoration: none">点击升级a>
shiro:lacksRole>
td>
tr>
table>
有指定权限
缺失指定权限
...
<td>
<a href="#" style="text-decoration:none">查看详情a>
<shiro:hasPermission name="user:delete">
<a href="#" style="text-decoration: none">删除a>
shiro:hasPermission>
<shiro:lacksPermission name="user:delete">
<a href="#" style="text-decoration: none" >无权删除a>
shiro:lacksPermission>
td>
...
jsp中允许自定义标签,所以可以根据需求 自定义一些shiro标签。
public class MyAllRoleTag extends RoleTag {
// jsp中使用:
private String name;//存储传入的角色参数
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
protected boolean showTagBody(String name) {
System.out.println("验证角色:"+name);
String[] roles = name.split(",");
Subject subject = getSubject();
for(String role:roles) {
if(!subject.hasRole(role)){
return false;
}
}
return true;
}
}
要放在
WEB-INF
目录下,名称任意
<taglib>
<tlib-version>1.1.2tlib-version>
<jsp-version>1.2jsp-version>
<short-name>zhjshort-name>
<uri>http://zhj.apache.org/tagsuri>
<description>Apache Shiro JSP Tag Library.description>
<tag>
<name>hasAllRolesname>
<tag-class>com.zhj.tag.MyAllRoleTagtag-class>
<body-content>JSPbody-content>
<description>Displays body content only if the current Subject (user)
'has' (implies) all the specified roles
description>
<attribute>
<name>namename>
<required>truerequired>
<rtexprvalue>truertexprvalue>
attribute>
tag>
taglib>
<%@ taglib prefix="zhj" uri="http://zhj.apache.org/tags" %>
有所有的角色2:manager,seller