Shiro入门(一)
0. Shiro是什么?
简单的来说
Apache Shiro 是一个强大灵活的开源安全框架,可以完全处理身份验证、授权、加密和会话管理。
shiro的四大基石
-
Authentication(认证):用户身份识别,通常被称为用户“登录”
-
Authorization(授权): 访问控制。比如某个用户是否具有某个操作的使用权限。
-
Session Management(会话管理): 特定于用户的会话管理,甚至在非web 或 EJB 应用程序。
-
Cryptography(加密): 在对数据源使用加密算法加密的同时,保证易于使用。
Shrio的三个理念(重要)
Subject:
当前用户,Subject 可以是一个人,但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它–当前和软件交互的任何事件。
SecurityManager:
管理所有Subject,SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成安全伞。
Realms:
用于进行权限信息的验证,我们自己实现。Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和/或授权(authorization)。
1. Shiro的简单使用
1.1 导入依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
1.2 创建一个测试类
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("liliya","123456","admin","user");
}
@Test
public void testAuthentication(){
//1. 构建Security环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(simpleAccountRealm);
//2. 主题提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject(); //获取当前的主体
//3. 模拟用户登陆
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("liliya","123456");
subject.login(usernamePasswordToken);
System.out.println("用户登录:"+(subject.isAuthenticated()?"成功!":"失败"));
//检查用户是否有权限
subject.checkRole("admin");
//退出登录
subject.logout();
System.out.println("用户登录:"+(subject.isAuthenticated()?"成功!":"失败"));
}
}

总的流程就是这样
- 先创建一个Realm,用于权限的验证
- 构建SecurityManager管理Realm
- 通过Shiro的SecurityUtils设置SecurityManager
- 通过SecurityUtils来获得当前对象Subject
- subject.login登录
如果登录失败会怎么样?
这里我故意打错,可以得到。
密码错误就是: IncorrectCredentialsException

用户不存在就是: UnknownAccountException

权限不存在:UnauthorizedException

2. 自定义Realm
搞一个配置类,继承 AuthorizingRealm ,要重写两个方法,一个是用来授权的,一个是用来认证的。。。
public class MyRealm extends AuthorizingRealm {
/**
* 授权
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
/**
* 认证
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
return null;
}
}
2.1 例子
public class MyRealm extends AuthorizingRealm {
HashMap<String, String> map = new HashMap<>();
{
map.put("liliya","123456");
map.put("admin","admin");
map.put("root","root");
super.setName("myRealm"); //配置当前realm的名字
}
/**
* 授权
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//获取用户名
String userName = (String) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
//假装从数据库拿到权限和角色
Set<String> roles = queryRolesByUserName(userName);
Set<String> permissions = queryPermissionByUserName(userName);
authorizationInfo.setRoles(roles);
authorizationInfo.setStringPermissions(permissions);
return authorizationInfo;
}
private Set<String> queryPermissionByUserName(String userName) {
HashSet<String> permissions = new HashSet<>();
permissions.add("add");
permissions.add("query");
permissions.add("update");
permissions.add("delete");
return permissions;
}
//假装从数据库拿到了权限
private Set<String> queryRolesByUserName(String userName) {
HashSet<String> roles = new HashSet<>();
roles.add("admin");
roles.add("user");
return roles;
}
/**
* 认证
* @param token
* @return
* @throws AuthenticationException
*/
@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(userName, password, "myRealm");
return authenticationInfo;
}
//假装从数据库查到了数据
private String getPasswordByUserName(String userName) {
return map.get(userName);
}
}
2.2 测试
public class AuthenticationTest3 {
@Test
public void auth(){
MyRealm myRealm = new MyRealm();
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(myRealm);
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
//模拟登录
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("liliya","123456");
subject.login(usernamePasswordToken);
System.out.println((subject.isAuthenticated())?"登录成功":"登录失败");
//检查用户是否是admin角色
subject.checkRole("admin");
//检查用户是否有add权限
subject.checkPermissions("add");
}
}
3. Shiro自带加密
String password = "123456";
String salt = new SecureRandomNumberGenerator().nextBytes().toString();
int times = 2; // 加密次数:2
String alogrithmName = "md5"; // 加密算法
String encodePassword = new SimpleHash(alogrithmName, password, salt, times).toString();
System.out.printf("原始密码是 %s , 盐是: %s, 运算次数是: %d, 运算出来的密文是:%s ",password,salt,times,encodePassword);
参考链接:
https://zhuanlan.zhihu.com/p/54176956

浙公网安备 33010602011771号