shiro入门
shiro
什么是shiro
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
shiro的功能
三个核心组件:Subject, SecurityManager 和 Realms.
Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。
Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。
入门小案例
- 引入shiro相关依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.3</version>
</dependency>
- 创建一个shiro.ini的配置文件,用来学习shiro书写权限相关的数据。
[users]
szl=123
lh=456
cs=789
随意设置几组用户信息,以键值对的方式设置。
- 新建一个TestAuthenticator类
/**
* @ClassName TestAuthenticator
* @Description TODO
* @Author szl
* @Date: Created in 9:33 2020/6/25
* @Version 1.0
**/
public class TestAuthenticator {
public static void main(String[] args) {
//创建安全管理对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//给安全管理器设置realm
securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
//给全局安全工具类设置安全管理器
SecurityUtils.setSecurityManager(securityManager);
//获取关键对象subject主体
Subject subject = SecurityUtils.getSubject();
//创建令牌token
UsernamePasswordToken token = new UsernamePasswordToken("szl","123");
try {
System.out.println("认证状态:"+subject.isAuthenticated());
subject.login(token);
System.out.println("认证状态:"+subject.isAuthenticated());
}catch (Exception e){
e.printStackTrace();
System.out.println("登陆失败");
}
}
}
自定义realm的实现,将认证的数据来源转为数据库
- 先创建一个realm类CustomerRealm
public class CustomerRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}
- 创建一个测试类TestCustomerRealmAuthenticator
public class TestCustomerRealmAuthenticator {
public static void main(String[] args) {
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(new CustomerRealm());
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("szl", "123");
try{
subject.login(token);
}catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("找不到该用户");
}catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("密码错误");
}
}
}
- 创建的realm中返回为null,所以测试类中报错为UnknownAccountException
- 在重写的方法doGetAuthenticationInfo中添加返回对象如下:
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String principal = (String) token.getPrincipal();
System.out.println(principal);
if ("szl".equals(principal)) {
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal,"123",this.getName());
return simpleAuthenticationInfo;
}
return null;
}
MD5加密和salt
- 使用MD5加密后的数值与加了salt以后已经使用hash多次散列后的完全不同。
public class TestShiroMd5 {
public static void main(String[] args) {
//使用MD5
Md5Hash md5Hash = new Md5Hash("123");
System.out.println(md5Hash.toHex());
//使用MD5+salt
Md5Hash md5hash1 = new Md5Hash("123", "qwe");
System.out.println(md5hash1.toHex());
//使用MD5+salt+hash
Md5Hash md5Hash2 = new Md5Hash("123", "qwe", 1024);
System.out.println(md5Hash2.toHex());
}
}
- 要实现MD5在shiro中的使用,则需要设置算法已经散列次数
public class TestCustomerMd5RealmAuthenticator {
public static void main(String[] args) {
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
CustomerMd5Realm realm = new CustomerMd5Realm();
//设置realm使用hash凭证匹配器
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//使用算法
hashedCredentialsMatcher.setHashAlgorithmName("md5");
//使用散列
hashedCredentialsMatcher.setHashIterations(1024);
realm.setCredentialsMatcher(hashedCredentialsMatcher);
defaultSecurityManager.setRealm(realm);
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("szl", "123");
try {
subject.login(token);
System.out.println("登录成功");
}catch(UnknownAccountException e){
e.printStackTrace();
System.out.println("用户名不存在");
}catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("密码错误");
}
}
}
- realm类中,同时也要有相关的配置, ByteSource.Util.bytes("qwe")用来设置salt的值.
public class CustomerMd5Realm extends AuthorizingRealm {
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String principal = (String) authenticationToken.getPrincipal();
if("szl".equals(principal)){
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal,
"514f70e88fd768b45c04f1c51871b784",
// "123",
ByteSource.Util.bytes("qwe"),
this.getName());
return simpleAuthenticationInfo;
}
return null;
}
}