Shiro 概念
安全应该是互联网公司的一道生命线,几乎任何的公司都会涉及到这方面的需求。在Java领域一般有Spring Security、Apache Shiro等安全框架,但是由于Spring Security过于庞大和复杂,大多数公司会选择Apache Shiro来使用。
Apache Shiro是一个功能强大、灵活的,开源的安全框架。
- 在使用Shiro 之前,大家做登录,权限什么的都是五花八门,各种花里胡哨的代码,不同系统的做法很有可能千差万别。
- 但是使用 Shiro 这个安全框架之后,大家做权限的方式都一致化了,这样的好处就是你的代码我看起来容易,我的代码你也好理解。
- Shiro 也比较成熟,基本上能满足大部分的权限需要。
Shiro能做什么呢?
- 验证用户身份
- 用户访问权限控制,比如:1、判断用户是否分配了一定的安全角色。2、判断用户是否被授予完成某个操作的权限
- 在非 web 或 EJB 容器的环境下可以任意使用Session API
- 可以响应认证、访问控制,或者 Session 生命周期中发生的事件
- 可将一个或以上用户安全数据源数据组合成一个复合的用户 “view”(视图)
- 支持单点登录(SSO)功能
- 支持提供“Remember Me”服务,获取用户关联信息而无需登录
…
等等——都集成到一个有凝聚力的易于使用的API。
1,先来个最简单的,两下就跑起来的基于ini配置文件的shiro 入门
shiro.ini文件
在src目录下新建 shiro.ini
这里面定义了和安全相关的数据: 用户,角色和权限
#定义用户
[users]
#用户名 zhang3 密码是 12345, 角色是 admin
zhang3 = 12345, admin
#用户名 li4 密码是 abcde, 角色是 产品经理
li4 = abcde,productManager
#定义角色
[roles]
#管理员什么都能做
admin = *
#产品经理只能做产品管理
productManager = addProduct,deleteProduct,editProduct,updateProduct,listProduct
#订单经理只能做订单管理
orderManager = addOrder,deleteOrder,editOrder,updateOrder,listOrder
User
准备用户类,用于存放账号密码
package com.how2java;
public class User {
private String name;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
TestShiro
准备3个用户,前两个能在 shiro.ini 中找到,第3个不存在
然后测试登录
接着测试是否包含角色
最后测试是否拥有权限
注:Subject 在 Shiro 这个安全框架下, Subject 就是当前用户
package com.how2java;
import java.util.ArrayList;
import java.util.List;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
public class TestShiro {
public static void main(String[] args) {
//用户们
User zhang3 = new User();
zhang3.setName("zhang3");
zhang3.setPassword("12345");
User li4 = new User();
li4.setName("li4");
li4.setPassword("abcde");
User wang5 = new User(); //wang5用户在shiro.ini中并没有配置
wang5.setName("wang5");
wang5.setPassword("wrongpassword");
List<User> users = new ArrayList<>();
users.add(zhang3);
users.add(li4);
users.add(wang5);
//角色们
String roleAdmin = "admin";
String roleProductManager ="productManager";
List<String> roles = new ArrayList<>();
roles.add(roleAdmin);
roles.add(roleProductManager);
//权限们
String permitAddProduct = "addProduct";
String permitAddOrder = "addOrder";
List<String> permits = new ArrayList<>();
permits.add(permitAddProduct);
permits.add(permitAddOrder);
//登陆每个用户
for (User user : users) {
if(login(user)) //判断用户能够登录(账号、密码),调用login()方法
System.out.printf("%s \t成功登陆,用的密码是 %s\t %n",user.getName(),user.getPassword());
else
System.out.printf("%s \t成功失败,用的密码是 %s\t %n",user.getName(),user.getPassword());
}
System.out.println("-------how2j 分割线------");
//判断能够登录的用户是否拥有某个角色
for (User user : users) {
for (String role : roles) {
if(login(user)) {
if(hasRole(user, role)) //调用hasRole()方法
System.out.printf("%s\t 拥有角色: %s\t%n",user.getName(),role);
else
System.out.printf("%s\t 不拥有角色: %s\t%n",user.getName(),role);
}
}
}
System.out.println("-------how2j 分割线------");
//判断能够登录的用户,是否拥有某种权限
for (User user : users) {
for (String permit : permits) {
if(login(user)) {
if(isPermitted(user, permit)) //调用isPermitted()方法
System.out.printf("%s\t 拥有权限: %s\t%n",user.getName(),permit);
else
System.out.printf("%s\t 不拥有权限: %s\t%n",user.getName(),permit);
}
}
}
}
private static boolean hasRole(User user, String role){
Subject subject = getSubject();
return subject.hasRole(role);
}
private static boolean isPermitted(User user, String permit){
Subject subject = getSubject();
return subject.isPermitted(permit);
}
private static Subject getSubject(){
//加载配置文件,并获取工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//获取安全管理者实例
SecurityManager sm = factory.getInstance();
//将安全管理者实例放入全局对象
SecurityUtils.setSecurityManager(sm);
//全局对象通过安全管理者生成Subject对象
Subject subject = SecurityUtils.getSubject();
return subject;
}
private static boolean login(User user){
Subject subject = getSubject();
//如果已经登录过了,退出
if(subject.isAuthenticated()) {
subject.logout();
}
//封装用户的数据
UsernamePasswordToken token = new UsernamePasswordToken(user.getName(),user.getPassword());
try{
subject.login(token);
}catch(AuthenticationException e){
return false;
}
return subject.isAuthenticated();
}
}
简单了解:
isAuthenticated():
用户身份识别,通常被称为用户“登录”
已验证(Authenticated):一个被验证的Subject是成功验证后(如登录成功)并存于当前session中,一个被认为验证过的对象调用subject.isAuthenticated()将返回真。
logout():
当你调用logout,任何现存的session将变为不可用并且所有的身份信息将消失(如:在web程序中,RememberMe的Cookie信息同样被删除)。
- Subject:当前用户,Subject 可以是一个人,但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它–当前和软件交互的任何事件。
- SecurityManager:管理所有Subject,SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成安全伞。
- UsernamePasswordToken:是最常见的用户名/密码的认证机制。
参考来源于:
http://how2j.cn/k/shiro/shiro-tutorial/1720.html 源代码来源how2java
http://blog.sina.com.cn/s/blog_804c1dce0102x15w.html
http://www.ityouknow.com/springboot/2017/06/26/springboot-shiro.html