Shiro安全框架的简单概括
简介
1.Apache的强大灵活的开源安全框架;
2.Shiro可以完成认证、授权、企业会话管理、缓存管理、安全加密、web集成等功能;
Shiro与Spring Security比较
Apache Shiro | Spring Secuti |
---|---|
简单、灵活、可脱离Spirng、粒度较粗 | 复杂、笨重、不可脱离Spring、粒度较细 |
Shiro的整体架构
注意:Shiro不会维护用户和维护权限;这些需要自己去设计/提供,然后通过相应的接口注入给Shiro即可。
环境的准备
创建一个基于Maven的项目,且准备好以下依赖
<dependencies>
<dependency>
<!--引入shiro的核心包-->
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
Shiro认证步骤
1、创建SecurityManager
2、主体提交认证
3、SecurityManager认证
4、Authentication认证
5、Relam验证
Shiro认证代码如下
package com.haibin.test;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;
public class AuthenticationTest {
SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
@Before
public void addUser() {
simpleAccountRealm.addAccount("Mark", "123456");
}
@Test
public void testAuthentication() {
//1、构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(simpleAccountRealm);
//2、主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("Mark", "123456");
subject.login(token);
System.out.println("isAuthentication:" + subject.isAuthenticated());
subject.logout();
System.out.println("isAuthentication:" + subject.isAuthenticated());
}
}
注意:Realm:域,Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。
Shiro授权步骤
1、创建SecurityManager
2、主体授权
3、SecurityManager授权
4、Authenrizer授权
5、Realm获取角色权限数据
Shiro授权代码如下
//代码大部分与认证的相同,只在以下两处做了修改
simpleAccountRealm.addAccount("Mark", "123456","admin","user");
//检查用户是否具备这个权限
subject.checkRoles("admin","user");
IniRealm的使用
//创建iniRealm对象,且读取这个文件
IniRealm iniRealm = new IniRealm("classpath:user.ini");
//1、构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//将iniRealm设置到SecurityManager环境中
defaultSecurityManager.setRealm(iniRealm);
//2、主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("Mark", "123456");
subject.login(token);
System.out.println("isAuthentication:" + subject.isAuthenticated());
subject.checkRole("admin");
subject.checkPermissions("user:delete","user:update");
user.ini文件
[users]
Mark=123456,admin
[roles]
admin=user:delete,user:update
JdbcRealm的使用
1、访问数据库,就要在pom.xml中添加数据库驱动,数据源的依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
2、用户表
3、角色表
4、主要代码
//设置一个数据源,所以先创建一个数据源对象
DruidDataSource dataSource = new DruidDataSource();
//要访问数据库,首先要想到访问数据库的三要素url,username,password
{
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("root");
}
@Test
public void testAuthentication() {
//创建jdbcRealm
JdbcRealm jdbcRealm = new JdbcRealm();
//jdbcRealm设置一个数据源
jdbcRealm.setDataSource(dataSource);
//设置为ture才不会查询权限数据
jdbcRealm.setPermissionsLookupEnabled(true);
//自定义用户表sql语句
String sql = "select password from test_user where username=?";
jdbcRealm.setAuthenticationQuery(sql);
//自定义角色表语句
String roleSql = "select role_name from test_user_role where username=?";
jdbcRealm.setUserRolesQuery(roleSql);
//1、构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//将jdbcRealm设置到SecurityManager环境中
defaultSecurityManager.setRealm(jdbcRealm);
//2、主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("xiaoming", "654321");
subject.login(token);
System.out.println("isAuthentication:" + subject.isAuthenticated());
/*
subject.checkRole("admin");
subject.checkRoles("admin","user");
subject.checkPermission("user:select");
*/
subject.checkRole("user");
}
自定义Realm
代码如下:
public class CustomRealm extends AuthorizingRealm {
Map<String, String> userMap = new HashMap<String, String>(16);
{
userMap.put("Mark", "123456");
super.setName("CustomRealm");
}
//授权的抽象方法
//参数是主体传过来的授权信息
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String userName= (String) principalCollection.getPrimaryPrincipal();
//从数据库或者缓存中获取角色数据
Set<String> roles=getRolesByUserName(userName);
//从数据或者缓存中获取权限数据
Set<String> permissions=getPermissionByUserName(userName);
SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setStringPermissions(permissions);
simpleAuthorizationInfo.setRoles(roles);
return simpleAuthorizationInfo;
}
private Set<String> getPermissionByUserName(String userName) {
Set<String> sets=new HashSet<String>();
sets.add("user:add");
sets.add("user:delete");
return sets;
}
private Set<String> getRolesByUserName(String userName) {
Set<String> sets=new HashSet<String>();
sets.add("admin");
sets.add("user");
return sets;
}
//认证的抽象方法
//参数是主体传过来的认证信息
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {
//1、从主体传过来的认证信息中获取用户名
String userName = (String) authenticationToken.getPrincipal();
//2、通过用户名到数据库中获取凭证
String password = getPasswordByUserName(userName);
if (password == null) {
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo
("Mark", "123456", "CustomRealm");
return authenticationInfo;
}
/*
省略时间,这里没有创建数据库,前面创建一个集合且赋值,模拟数据库
*/
private String getPasswordByUserName(String userName) {
return userMap.get(userName);
}
测试类代码:
public class CustomRealmTest {
@Test
public void testAuthentication() {
//创建customRealm对象
CustomRealm customRealm=new CustomRealm();
//1、构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//将customRealm加入到这个环境中
defaultSecurityManager.setRealm(customRealm);
//2、主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("Mark", "123456");
subject.login(token);
System.out.println("isAuthentication:" + subject.isAuthenticated());
/*
subject.checkRole("admin");
subject.checkPermissions("user:delete","user:update");
*/
subject.checkRole("admin");
subject.checkPermissions("user:add","user:delete");
}
Shiro的加密加盐
代码在自定义的realmd的基础上做以下修改
public static void main(String[] args){
String md5Hash=new Md5Hash("123456","Mark").toHex();
System.out.println(md5Hash.toString());
}
运行main()方法得到加密后密码的字符串复制到userMap中
Map<String, String> userMap = new HashMap<String, String>(16);
{
userMap.put("Mark","283538989cef48f3d7d8a1c1bdf2008f ");
super.setName("CustomRealm");
}
加盐后需要在认证的抽象方法中添加的代码
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo
("Mark", "123456", "CustomRealm");
//加盐后需要添加的代码
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("Mark"));
return authenticationInfo;
然后在测试类SecurityManager环境后添加一下代码即可
//加密
//创建一个matcher对象
HashedCredentialsMatcher matcher=new HashedCredentialsMatcher();
//设置加密的名称
matcher.setHashAlgorithmName("md5");
//设置加密的次数
matcher.setHashIterations(1);
//把matcher对象设置金自定义realm中
customRealm.setCredentialsMatcher(matcher);
##总结的有点粗糙,还请多多包容,刚开始写这类文章。