shiro
为什么要⽤shiro:
1.项⽬中的密码是否可以明⽂存储?
2.是否任意访客,⽆论是否登录都可以访问任何功能?
3.项⽬中的各种功能操作,是否是所有⽤户都可以随意使⽤?
综上,当项⽬中的某些功能被使⽤时,需要进⾏安全校验,进⽽保证整个系统的运⾏秩序。
shiro是什么
- Apache Shiro 是 Java 的⼀个安全(权限)框架。
Shiro 可以轻松的完成:身份认证、授权、加密、会话管理等功能 - Shiro 可以⾮常容易的开发出⾜够好的应⽤,其不仅可以⽤在JavaSE 环境,也可以⽤在 JavaEE 环境。
功能强⼤且易⽤,可以快速轻松地保护任何应⽤程序 ( 从最⼩的移动应⽤程序到最⼤的Web和企业应⽤程序。) - ⽅便的与Web 集成和搭建缓存。
功能介绍
- Authentication:身份认证/登录,验证⽤户是不是拥有相应的身份;
- Authorization:授权,即权限验证,验证某个已认证的⽤户是否拥有某个权限;即判断⽤户是否能进⾏什么操作。如:验证某个⽤户是否拥有某个⻆⾊。或者细粒度的验证某个⽤户对某个资源是否具有某个权限;
- Session Manager:会话管理,即⽤户登录后就是⼀次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境,也可以是 Web 环境的;
- Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,⽽不是明⽂存储;
- Web Support:Web ⽀持,可以⾮常容易的集成到Web 环境;
- Caching:缓存,⽐如⽤户登录后,其⽤户信息、拥有的⻆⾊/权限不必每次去查,这样可以提⾼效率;
- Remember Me:记住我,这个是⾮常常⻅的功能,即⼀次登录后,下次再来的话可以⽴即知道你是哪个⽤户
工作流程
- • 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,( 数据访问⼊⼝ )
框架了解
- 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 提供了⼀些常⻅的加密组件⽤于如密码加密/解密。
1,shiro认证
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.3</version>
</dependency>
[users]
xiaochen=123
zhangsan=123456
lisi=789
public static void main(String[] args) {
//创建安全管理器
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//给安全管理器设置realm
securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
//SecurityUtils全局安全工具类,有认证和退出的相关方法
SecurityUtils.setSecurityManager(securityManager);
//创建关键对象
Subject subject = SecurityUtils.getSubject();
//创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","123456");
try{
System.out.println("认证状态:上"+subject.isAuthenticated());
subject.login(token); //用户认证
System.out.println("认证状态:下"+subject.isAuthenticated());
}catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("认证失败,用户名不存在");
}catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("认证失败,密码不正确");
}
}
shiro基础二:自定义realm
/**
* 自定义realm实现 将认证、授权的数据来源转为数据库的实现
*/
public class CustomerRealm extends AuthorizingRealm {
//授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
//认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//在token中获取用户名
String principal = (String) token.getPrincipal();
System.out.println(principal);
//更具身份信息用mybatis查询数据库信息
if("zhangsan".equals(principal)){
//参数一:返回数据库中正确用户名
//参数二:返回数据库中的密码
//参数散:提供当前realm的名字 this.getName();
SimpleAuthenticationInfo simpleAuthenticationInfo =
new SimpleAuthenticationInfo(principal,"123456",this.getName());
return simpleAuthenticationInfo;
}
return null;
}
}
public class TestCustomerRealmAuthentocator {
public static void main(String[] args) {
//创建defaultSecurityManager
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//设置自定义realm
defaultSecurityManager.setRealm(new CustomerRealm());
//将安全工具类设置defaultSecurityManager
SecurityUtils.setSecurityManager(defaultSecurityManager);
//获取subject
Subject subject = SecurityUtils.getSubject();
//创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","123456");
try {
subject.login(token);
System.out.println("登录成功");
} catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("密码不正确");
} catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("用户名不正确");
}
}
}
shiro基础三:MD5
public class TestMD5 {
public static void main(String[] args) {
//创建一个md5算法
Md5Hash md5Hash = new Md5Hash("123456");
System.out.println(md5Hash);
//使用md5加盐
Md5Hash md5Hash1 = new Md5Hash("123456","qwea");
System.out.println(md5Hash1);
//md5+salt+hash散列
Md5Hash md5Hash2 = new Md5Hash("123456", "qwea", 123);
System.out.println(md5Hash2);
}
}
//授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
//认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String principal = (String)token.getPrincipal();
if("zhangsan".equals(principal)){
return new SimpleAuthenticationInfo(
principal,
"4d28cf9e49d3cf8ae8fe8fea9437ea3f", //MD5+随机盐+散列后的密码
ByteSource.Util.bytes("qwea"),//随机盐
this.getName());
}
return null;
}
}
public static void main(String[] args) {
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
CustomerMd5Realm realm = new CustomerMd5Realm();
//设置realm使用hash凭证匹配器
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("md5");
credentialsMatcher.setHashIterations(123); //告诉凭证匹配器散列多少次
realm.setCredentialsMatcher(credentialsMatcher);
defaultSecurityManager.setRealm(realm);
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","123456");
try {
subject.login(token);
System.out.println("登录成功");
} catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("密码不正确");
} catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("用户名不正确");
}
}
}
2,shiro授权
关键对象
授权过程
授权方式
权限字符串
授权编码
开发授权
public class CustomerRealm extends AuthorizingRealm {
//授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("============");
String primaryPrincipal = (String)principals.getPrimaryPrincipal();
System.out.println("身份信息:"+primaryPrincipal);
//根据身份信息,在数据库中获取当前角色信息
//将数据库中查询的角色信息复制给权限对象
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRole("admin");
simpleAuthorizationInfo.addRole("user");
//将数据库中查询权限信息赋值给权限对象
simpleAuthorizationInfo.addStringPermission("user:*:01");
simpleAuthorizationInfo.addStringPermission("product:create");
return simpleAuthorizationInfo;
}
//认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//在token中获取用户名
String principal = (String) token.getPrincipal();
System.out.println(principal);
//更具身份信息用mybatis查询数据库信息
if("zhangsan".equals(principal)){
//参数一:返回数据库中正确用户名
//参数二:返回数据库中的密码
//参数散:提供当前realm的名字 this.getName();
SimpleAuthenticationInfo simpleAuthenticationInfo =
new SimpleAuthenticationInfo(principal,"123456",this.getName());
return simpleAuthenticationInfo;
}
return null;
}
}
public class TestCustomerRealmAuthentocator {
public static void main(String[] args) {
//创建defaultSecurityManager
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//设置自定义realm
defaultSecurityManager.setRealm(new CustomerRealm());
//将安全工具类设置defaultSecurityManager
SecurityUtils.setSecurityManager(defaultSecurityManager);
//获取subject
Subject subject = SecurityUtils.getSubject();
//创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","123456");
try {
subject.login(token);
System.out.println("登录成功");
} catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("密码不正确");
} catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("用户名不正确");
}
//认证用户进行授权
if(subject.isAuthenticated()){
//基于单角色进行权限授权
System.out.println(subject.hasRole("admin"));
//基于多角色的权限控制
System.out.println(subject.hasAllRoles(Arrays.asList("admin","user")));
//是否有其中一个角色
boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "user", "student"));
for(boolean aBoolean : booleans){
System.out.println(aBoolean);
}
//基于权限支付窜的访问控制, 资源标识符:操作:资源类型
System.out.println("基于权限支付窜的访问控制:"+subject.isPermitted("user:update:01"));
System.out.println("基于权限支付窜的访问控制:"+subject.isPermitted("product:create:01"));
//分别具有那些权限
boolean[] permitted = subject.isPermitted("user:*:01", "order:*:10");
for(boolean b : permitted){
System.out.println(b);
}
//同时具有那些权限
boolean permittedAll = subject.isPermittedAll("user:*:01", "product:*");
System.out.println(permittedAll);
}
}
}