什么是Shiro
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。
使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
架构图-Shiro外部
Subject,理解为当前用户的安全操作
ShiroSecurityManager,Shiro框架的核心。安全管理器,所有与安全有关系的操作,都会与ShiroSecurityManager进行交互,并且管理着所有的Subject。
Realm,充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。
架构图-Shiro内部
Authenticator,负责主体认证。
Authorizer,用来决定主体是否有权限进行操作
Realms,可以有1个或多个,安全实体的数据源。
SessionManager,用来管理主体与应用之间交互的数据。
SessionDao,用于会话的增删改查。
CacheManager,用来管理用户、角色、权限的缓存
Cryptography,密码模块,shiro提供了一套加密/解密的组件。
Shiro常用名词
Subject 主体
Security 安全
Realm 领域、范围
Authenticator 认证器
Authentication 认证
Authorizer 授权器
Authorization 授权
Cryptography 密码、加密
Credential 证书、凭证
Matcher 匹配器
Principal 身份
Shiro配置文件
ini文件中主要配置有四大类:main,users,roles,urls
main,主要配置shiro的一些对象,例如securityManager ,Realm, authenticator,authcStrategy 等等。
users,允许配置一组静态的用户,包含用户名,密码,角色,一个用户 可以有多个角色,可以配置多个角色。
roles,将角色和权限关联起来。
urls, 配置主要在web应用中,格式为:url=拦截器[参数],拦截器[参 数]……
shiro.ini文件放在classpath下,shiro会自动查找,其中格式是key/value 键值对配置。
Realm
Shiro自带的IniRealm,IniRealm从ini配置文件中读取用户的信息。
[main]
#配置Realm
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
#配置数据源
dataSource = com.mchange.v2.c3p0.ComboPooledDataSource
dataSource.driverClass = com.mysql.jdbc.Driver
dataSource.jdbcUrl = jdbc:mysql://localhost:3306/test? serverTimezone=GMT&characterEncoding=UTF-8
dataSource.user = root
dataSource.password =123456
jdbcRealm.dataSource = $dataSource
#将Realm注入给SecurityManager
securityManager.realm = $jdbcRealm
如果使用的是JdbcRealm,那么数据表名和字段名都必须写成users、username、password,因为JdbcRealm的底层源码中就已经对表名和字段名做出了规定。
自定义Realm,登录认证
自定义Realm,可以注入给securityManager更加灵活的安全数据源。也就是说我们可以随意起数据表名以及字段名称了。
1、导入jar包。
shiro-all-1.3.2.jar
junit-4.9.jar
log4j-1.2.17.jar
shiro-all-1.3.2.jar
slf4j-api-1.7.5.jar
slf4j-log4j12-1.7.5.jar
com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar
commons-beanutils-1.9.2.jar
commons-logging-1.1.1.jar
2、创建Shiro.ini配置文件。
3、创建类继承抽象方法AuthenticatingRealm,需要重写方法。该方法可以理解为是认证方法。也可以去继承AuthorizingRealm,该类下面有两个方法
需要重写,一个是认证方法,一个是授权方法。当通过认证方法后,可以对该访问进行授权。
4、因为是自定义Realm,需要自己编写代码去操作数据库,这一步就创建数据库连接对象操作数据库进行查询。
5、将查询结果存储到临时变量中,并将结果返回。
6、编写测试类测试。
认证执行流程
获取ini配置文件,创建securityManager
subject执行login(token)提交认证,最终由securityManager调用Authenticator
Authenticator调用realm查询用户信息
realm根据token(usernamePasswordToken)中的账号查询用户信息,如果找不到则给Authenticator返回null,如果找到realm则再去匹配密码,匹配密码成功则认证通过。
MD5加密、加盐与迭代 01
public class MD5Test {
@Test
public void testMD5(){
//MD5加密,创建一个由Shiro提供的Md5Hash类。
Md5Hash md5Hash = new Md5Hash("123");
System.out.println(md5Hash);
//MD5加盐
Md5Hash md5Hash2 = new Md5Hash("123", "随机字符串");
System.out.println(md5Hash2);
//迭代
Md5Hash md5Hash3 = new Md5Hash("123", "随机字符串", 2);
System.out.println(md5Hash3);
}
}
//输出结果
202cb962ac59075b964b07152d234b70
4e7fbf83ecb3d4f22c47722dc964a422
da420611b5a4dd7510816745803b9c98
MD5凭证匹配器实现
/**
*将迭代加密后的结果存储到数据库中,再查询。
*/
public class CustomRealm extends AuthenticatingRealm{
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//提取变量
String principal =null;
String credentials=null;
String salt = null;
ResultSet rs = null;
Statement stm = null;
Connection conn = null;
//加载驱动
try {
DriverManager.registerDriver(new Driver());
//获取连接对象
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test? serverTimezone=GMT&characterEncoding=UTF-8", "root", "123456");
//创建操作对象
stm = conn.createStatement();
//执行sql语句
String sql = "select userName,passwd,passwd_salt from starLogin";
rs = stm.executeQuery(sql);
//循环遍历
while(rs.next()){
principal = rs.getString("userName");
credentials = rs.getString("passwd");
salt = rs.getString("passwd_salt");
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stm != null){
try {
stm.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 重写的为接口,需要创建它的实现类。SimpleAuthenticationInfo构造器重载方法中需要有一个ByteSource类型的加密盐,
需要对其进行数据库中获取到的字符串的盐进行转换。
*/
ByteSource newSalt = ByteSource.Util.bytes(salt);
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal, credentials, newSalt,"customRealm");
return simpleAuthenticationInfo;
}
}
Shiro配置文件
[main]
#配置凭证匹配器
credentialsMatcher = org.apache.shiro.authc.credential.HashedCredentialsMatcher
#设置凭证匹配器的相关属性
credentialsMatcher.hashAlgorithmName = MD5
#迭代次数
credentialsMatcher.hashIterations = 2
#配置Realm
customRealm = com.sxt.realms.CustomRealm
#配置Realm的凭证属性器
customRealm.credentialsMatcher = $credentialsMatcher
#将Reaml注入给SucurityManager
securityManager.realm = $customRealm
授权
/**
* 授权,访问控制,是对资源的访问管理的过程。即对于 认证通过的用户,授予他可以访问某些资源的权限。
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
//提取变量
String roleName= null;
String remark = null;
ResultSet rs = null;
Statement stm = null;
Connection conn = null;
//加载驱动
try {
DriverManager.registerDriver(new Driver());
//获取连接对象
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/shiro? serverTimezone=GMT&characterEncoding=UTF-8", "root", "123456");
//创建操作对象
stm = conn.createStatement();
//执行sql语句
// String sql = "select name from role";
String sql = "select remark from permission";
rs = stm.executeQuery(sql);
//循环遍历
if(rs.first()){
// roleName = rs.getString("name");
remark = rs.getString("remark");
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stm != null){
try {
stm.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// info.addRole(roleName);
info.addStringPermission(remark);//把权限添加进去
return info;
}
/**
* 测试类
*
*/
public class AuthorizationTest {
@Test
public void testAuthorization(){
//1、构建SecurityManager工厂
IniSecurityManagerFactory SecurityManagerFactory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2、通过SecurityManagerFactory获取SecurityManager实例
SecurityManager securityManager = SecurityManagerFactory.getInstance();
//3、将securityManager设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
//4、获取Subject实例
Subject subject = SecurityUtils.getSubject();
//5、创建用户名密码验证令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","123456");
//6、进行身份验证
subject.login(token);
//7、判断是否认证通过
System.out.println("认证是否通过:"+subject.isAuthenticated());
//授权操作
//基于角色授权
System.out.println("角色授权:"+subject.hasRole("管理员"));
//资源授权
System.out.println("权限授权:"+subject.isPermitted("一级菜单,基本设置操作权限"));
}
}
Shiro.ini配置文件
[main]
#配置Realm
customRealm = com.sxt.realms.CustomRealm
#将Realm注入给SecurityManager
securityManager.realm = $customRealm