Apache Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理功能,可为任何应用提供安全保障。
Shiro的具体功能点如下:
(1)身份认证/登录,验证用户是不是拥有相应的身份;
(2)授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
(3)会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;
(4)加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
(5)Web支持,可以非常容易的集成到Web环境;
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
(6)shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
(7)提供测试支持;
(8)允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
(9)记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
二 身份认证Authentication
1 工作流程
2 认证主体 Subject
Subject 认证主体包含两个信息:
Principals:身份。可以是用户名,邮件,手机号码等等,用来标识一个登录主体身份;
Credentials:凭证。常见有密码,数字证书等等
在Shiro中可以在.ini文件中指定一个认证主体,也可以从数据库中取。
ini文件写法:
[users]
zhangsan=12345
lisi=12345
3 realm
realm就是一个域,Shiro就是从realm中获取验证数据的,比如,上一节中提到的.ini文件,realm有很多种,如text realm、jdbc realm、jndi realm、自定义realm等。
4 程序实现
1)在maven中引入使用的jar
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.5</version>
</dependency>
2)代码:
// 读取配置文件,初始化SecurityManager工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
// 获取securityManager实例
SecurityManager securityManager = factory.getInstance();
// 把securityManager实例绑定到SecurityUtils
SecurityUtils.setSecurityManager(securityManager);
// 创建token令牌,用户名/密码
UsernamePasswordToken token = new UsernamePasswordToken("csdn", "123");
// 得到当前执行的用户
Subject currentUser = SecurityUtils.getSubject();
try{
// 身份认证
currentUser.login(token);
System.out.println("身份认证成功!");
}catch(AuthenticationException e){
e.printStackTrace();
System.out.println("身份认证失败!");
}
// 退出
currentUser.logout();
5 jdbc realm
1)配置文件写法
[main]
#数据源选择的是c3p0
dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
dataSource.driverClass=com.mysql.jdbc.Driver
dataSource.jdbcUrl=jdbc:mysql://localhost:3306/db_shiro
dataSource.user=root
dataSource.password=renr1981
#定义一个jdbc的realm,取名为jdbcRealm
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
#jdbcRealm中有个属性是dataSource,选择我们上边定义的dataSource
jdbcRealm.dataSource=$dataSource
#SecurityManager中的realm选择上面定义的jdbcRealm
securityManager.realms=$jdbcRealm
2)数据库中建立表users
users中字段为username、password
注意:表名必须为users、字段必须包括username和password,且字段名不能修改
三 自定义realm认证
如果针对相对简单的系统,使用.ini文件包括用户信息,角色信息,权限信息等是可行的。但是实际项目中,各种信息都是存放在数据库中,此时需要使用自定义realm
1 配置文件
custome_realm.ini
[main]
#自定义realm
customRealm=com.rr.realm.CustomRealm
#设置realm值,相当于注入
securityManager.realms=$customRealm
2 自定义realml类
自定义realm需要继承AuthorizingRealm类
重写如下两个方法:
doGetAuthenticationInfo()方法:用来验证当前登录的用户,获取认证信息
doGetAuthorizationInfo()方法:用来为当前登陆成功的用户授予权限和角色(已经登陆成功了)
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// TODO Auto-generated method stub
// token表示用户输入的内容
// 1从token中取出身份信息
String userCode = (String) token.getPrincipal();
// 2根据用户输入的用户名从数据库查询对应数据,如果查询不到返回null,如果查询到,获取密码进行判断
// 本例模拟从数据库查询到密码
String password = "123";
// 3返回认证信息AuthenticationInfo
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
userCode, password, this.getName());
return simpleAuthenticationInfo;
}
3 测试
// 读取配置文件,初始化SecurityManager工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory(classpath:customer_realm.ini);
4 hash算法
hash(散列)算法一般用于生成一段文本的摘要信息,散列算法不可逆,不同的内容获取相同的hash值的概率非常小,通常用于对密码进行处理,提高安全性。常用的散列算法有MD5、SHA-1。
使用散列算法时,还可提供一个salt(盐)值,然后与原始内容生成摘要信息,这样做的目的是为了更安全
1)shiro中的hash处理
public static void main(String[] args) {
//原始 密码
String source = "123";
//盐值
String salt = null;
//散列次数
int hashIterations = 1;
//构造方法中:
//第一个参数:明文,原始密码
//第二个参数:盐值,可以使用随机数
//第三个参数:hash的次数,比如2次,相当 于md5(md5(''))
Md5Hash md5Hash = new Md5Hash(source, salt, hashIterations);
String password_md5 = md5Hash.toString();
System.out.println(password_md5);
//另外的方式,第一个参数:指定hash算法
SimpleHash simpleHash = new SimpleHash("md5", source, salt, hashIterations);
System.out.println(simpleHash.toString());
}
[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#hash算法
credentialsMatcher.hashAlgorithmName=md5
#次数
credentialsMatcher.hashIterations=1
customRealm=com.rr.realm.CustomRealmMd5
#设置realm的凭证匹配器
customRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$customRealm
三 授权Authorization
权限认证,也叫访问控制,即在应用中控制谁能访问哪些资源
1 权限认证的要素
最核心的三个要素是:权限,角色和用户
权限(permission):即操作资源的权利,比如访问某个页面,以及对某个模块的数据的添加,修改,删除,查看的权利;
角色(role):指的是用户担任的的角色,一个角色可以有多个权限;
用户(user):在Shiro 中,代表访问系统的用户
2 访问控制
1)基于角色的访问控制
授权过程是通过判断角色来完成的,哪个角色可以做这件事
2)基于资源(权限)的访问控制
3 程序实现
1)realm文件
#用户,role表示各个角色
[users]
Tom=123,role1,role2,role3
Jerry=123,role1,role2
#定义不同角色都拥有哪些权限
[roles]
role1=user:select
role2=user:add,user:update
role3=user.delete
2)测试代码
@Test
public void testHasRole() {
String configFile = "classpath:shiro_role.ini";
String username = "csdn2";
String password = "123";
Subject currentUser = ShiroUtil.login(configFile, username, password);
//测试hasRole
System.out.println(currentUser.hasRole("role2")? "有role2这个角色" : "没有role2这个角色");
//测试hasRoles
boolean[] results = currentUser.hasRoles(Arrays.asList("role1","role2","role3"));
System.out.println(results[0]? "有role1这个角色" : "没有role1这个角色");
System.out.println(results[1]? "有role2这个角色" : "没有role2这个角色");
System.out.println(results[2]? "有role3这个角色" : "没有role3这个角色");
//测试hasAllRoles
System.out.println(currentUser.hasAllRoles(Arrays.asList("role1","role2","role3")));
currentUser.logout();
}
@Test
public void testHasPermit() {
String configFile = "classpath:shiro_role.ini";
String username = "csdn1";
String password = "123";
Subject currentUser = ShiroUtil.login(configFile, username, password);
//测试hasRole
System.out.println(currentUser.isPermitted("user:select")? "有select这个权限" : "没有select这个权限");
//测试hasRoles
boolean[] results = currentUser.isPermitted(new String[]{"user:select","user:add","user:update"});
System.out.println(results[0]? "有user:select这个角色" : "没有user:select这个角色");
System.out.println(results[1]? "有user:add这个角色" : "没有user:add这个角色");
System.out.println(results[2]? "有user:update这个角色" : "没有user:update这个角色");
currentUser.logout();
}
简单的就这样,写完了