Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能。谈及shiro必然少不了一张图和三个关键词语:Subject, SecurityManager 和 Realms。
简单来说可以总结为:
Subject:用户提交的请求
SecurityManager:shiro核心代码
Realms:shrio沟通数据库和缓存的一个中间桥梁(分为三类:iniRealm、jdbcRealm、自定义Realm)
- 一个简单的shiro程序步骤
// 指定认证的数据
SimpleAccountRealm accountRealm = new SimpleAccountRealm();
// 模拟添加用户
accountRealm.addAccount("xingdo", "123456","admin","user");
// 构建SecurityManager对象
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(accountRealm);
// 4.主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);// 构建SecurityManager环境
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("xingdo", "123456");
subject.login(token);//模拟登录
System.out.println("是否执行了认证"+subject.isAuthenticated());
//subject.logout();//模拟退出
//System.out.println("是否执行了认证"+subject.isAuthenticated());
subject.checkRoles("admin","user");//验证角色权限
简单逻辑为:
- 指定认证的数据
- 模拟添加用户
- 构建SecurityManager对象
- 主体提交认证请求
- SecurityManager认证
- Authenticator认证
- Realm认证
上面说到Realm认证分为三种:iniRealm、jdbcRealm、自定义Realm,下面编写相关代码(由于jdbcRealm大同小异,不列出代码了就)
iniRealm代码
public class iniRealmTest {
public static void main(String[] args) {
IniRealm iniRealm = new IniRealm("classpath:shirotest/user.ini");
// 1:构建SecurityManager对象,还有一个jdbcrealm不一一演示了
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(iniRealm);
// 2.主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);// 构建SecurityManager环境
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("xingdo", "123456");
subject.login(token);// 模拟登录
System.out.println("是否执行了认证" + subject.isAuthenticated());
subject.checkRole("admin");
subject.checkPermissions("user:delete","user:select1");
}
}
在resources下新建文件夹shirotest,再新建文件user.ini,代码如下
[users]
xingdo:123456,admin
[roles]
admin=user:delete,user:update,user:insert,user:select1
简介:
- 指定认证数据有一些改变,去user.ini下验证数据,本人路径为:shirotest/user.ini,不一样的可自行更改
- 将待验证数据交由SecurityManager处理:defaultSecurityManager.setRealm(iniRealm);
自定义Realm
public class myRealm extends AuthorizingRealm {
Map<String, String> userMap = new HashMap<String, String>();
{
userMap.put("xingdo", "a0bc2e2dd7e719d5713a4b8e54a274b7");
super.setName("myRealm");
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 用来做授权
String username = (String) principals.getPrimaryPrincipal();
//从数据库中或者缓存中获取角色数据
Set<String> roles = getRolesByUsername(username);
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setRoles(roles);
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 用来做认证
// 1,从主体传过来的认证信息中获取用户名,
String username = (String) token.getPrincipal();
// 2,通过用户名到数据库中获得密码
String password = getpasswordByUsername(username);
if (password == null) {
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("xingdo", password, "myRealm");
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("xingdo"));//加盐的方法
return authenticationInfo;
}
// 模拟数据库
private String getpasswordByUsername(String username) {
return userMap.get(username);
}
private Set<String> getRolesByUsername(String username){
Set<String> roles = new HashSet<String>();
roles.add("admin");
roles.add("user");
return roles;
}
}
myRealm代码:
public class myRealmTest {
public static void main(String[] args) {
myRealm myRealm = new myRealm();
// 构建SecurityManager对象,
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(myRealm);
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("md5");//加密名称
credentialsMatcher.setHashIterations(1);//加密次数
myRealm.setCredentialsMatcher(credentialsMatcher);
// 主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);// 构建SecurityManager环境
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("xingdo", "123456");
subject.login(token);// 模拟登录
System.out.println("是否执行了认证" + subject.isAuthenticated());
subject.checkRole("admin");
// subject.checkPermissions("user:delete", "user:select1");
}
}
整体逻辑:
- 新建文件myRealm继承AuthorizingRealm并且实现doGetAuthorizationInfo方法和doGetAuthenticationInfo方法。其中doGetAuthorizationInfo用来做用来做授权,doGetAuthenticationInfo用来做认证。
- 新建myRealmTest类,引用自定义的myRealm
doGetAuthorizationInfo、doGetAuthenticationInfo方法整体逻辑:
- 从主体传过来的认证信息中获取用户名,
- 从数据库中或者缓存中获取角色数据
- 构建SimpleAuthorizationInfo对象
- 将角色信息存放到SimpleAuthorizationInfo对象中并且返回信息
由于本次没有做mybatis集成,无法链接数据库,整体逻辑均使用的模拟数据库。将在下一篇文章中列出shiro与SpringBoot、mybatis的集成
至此整体shrio简单使用已经完成。现在互联网安全越来越重视,尤其是用户密码更是重中之重,那么shiro是怎么实现密码加密的呢?
shiro加密部分代码(该部分代码在myRealmTest中体现)
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("md5");//加密名称
credentialsMatcher.setHashIterations(1);//加密次数
myRealm.setCredentialsMatcher(credentialsMatcher);
MD5虽然无法反向解密,但是由于现在是大数据时代,将一些常见的密码和对应的加密字符串存放起来,根据字符串可以查找出未加密密码;这种情况加shrio推出了一种加”盐“的概念。即:在md5的基础上,新增一个字符串,用新增加的字符串和md5规则去加密。代码实现如下
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("xingdo"));//加盐的方法
以上代码在myRealmTest中体现,xingdo即新增的字符串