shiro学习记录
shiro架构
从外部来看
- Subject: 应用代码直接交互的对象,也就是说 Shiro 的对外
API 核心就是 Subject。Subject 代表了当前“用户”, 这个用户不一定
是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,
机器人等;与 Subject 的所有交互都会委托给 SecurityManager;
Subject 其实是一个门面,SecurityManager 才是实际的执行者; - SecurityManager: 安全管理器;即所有与安全有关的操作都会与
SecurityManager 交互;且其管理着所有 Subject;可以看出它是 Shiro
的核心,它负责与 Shiro 的其他组件进行交互,它相当于 SpringMVC 中
DispatcherServlet 的角色; - Realm: Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说
SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户
进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色/
权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource;
从外部来看
Subject:任何可以与应用交互的“用户”;
- SecurityManager : 相当于SpringMVC 中的 DispatcherServlet;是 Shiro 的心脏;
所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进
行认证、授权、会话及缓存的管理。 - Authenticator: 负责 Subject 认证,是一个扩展点,可以自定义实现;可以使用认证
策略(Authentication Strategy),即什么情况下算用户认证通过了; - Authorizer: 授权器、即访问控制器,用来决定主体是否有权限进行相应的操作;即控
制着用户能访问应用中的哪些功能; - Realm: 可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体
的;可以是JDBC 实现,也可以是内存实现等等;由用户提供;所以一般在应用中都需要
实现自己的 Realm; - SessionManager: 管理 Session 生命周期的组件;而 Shiro 并不仅仅可以用在 Web
环境,也可以用在如普通的 JavaSE 环境; - CacheManager: 缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据
基本上很少改变,放到缓存中后可以提高访问的性能 - Cryptography: 密码模块,Shiro 提高了一些常见的加密组件用于如密码加密/解密。
示例代码-controller
@Controller
@RequestMapping("/shiro")
public class ShiroHandler {
@Autowired
private ShiroService shiroService;
@RequestMapping("/login")
public String login(String username, String password) {
Subject currentUser = SecurityUtils.getSubject();
if (!currentUser.isAuthenticated()) {
// 把用户名和密码封装为UsernamePasswordToken对象
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
// 设置记住密码
token.setRememberMe(true);
try {
// 执行登陆
currentUser.login(token);
} catch (AuthenticationException ae) {
System.out.println("登陆失败:" + ae.getMessage());
}
}
return "redirect:/list.jsp";
}
}
示例代码-reaml
public class ShiroRealm extends AuthorizingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("[FirstRealm] doGetAuthenticationInfo");
// 1.把AuthenticationToken转化为UsernamePassWordToken
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
// 2.从UsernamePasswordToken中获取userName
String username = upToken.getUsername();
// 3.调用数据库方法,从数据库中根据userName查询用户信息(这里目前只做显示)
System.out.println("从数据库中获取 username: " + username + " 所对应的用户信息.");
// 4.若用户不存在,则抛出UnknownAccountException异常
if ("unknow".equals(username)) {
throw new UnknownAccountException("用户不存在");
}
// 5.根据用户信息,决定是否需要抛出其他AuthenticationException异常
if ("monster".equals(username)) {
throw new LockedAccountException("用户被锁定");
}
// 6. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo
// 以下信息是从数据库中获取的.
// 1). principal: 认证的实体信息. 可以是 username, 也可以是数据表对应的用户的实体类对象.
Object principal = username;
// 2). credentials: 密码.
Object credentials = null;
if ("admin".equals(username)) {
credentials = "038bdaf98f2037b31f1e75b5b4c9b26e";
} else if ("user".equals(username)) {
credentials = "098d2c478e9c11555ce2823231e02ec1";
}
// 3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可
String realmName = getName();
// 4).盐值(这里使用用户名,因为用户名是唯一的)
ByteSource credentialsSalt = ByteSource.Util.bytes(username);
SimpleAuthenticationInfo info = null;// new SimpleAuthenticationInfo(principal, credentials, realmName);
info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
return info;
}
/**
* 测试加密1024次后结果: 不加盐--fc1709d0a95a6be30bc5926fdb7f22f4
* 加盐admin:038bdaf98f2037b31f1e75b5b4c9b26e
* user:098d2c478e9c11555ce2823231e02ec1
*
* @param args
*/
public static void main(String[] args) {
String hashAlgorithmName = "MD5";
Object credentials = "123456";
Object salt = "admin";
int hashIterations = 1024;
Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
System.out.println(result);
}
/**
* 授权会被shiro回调的方法
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("这里是回调函数:doGetAuthorizationInfo");
// 1. 从principalsCollection中获取登录用户的信息
Object principal = principals.getPrimaryPrincipal();
// 2. 利用登录的用户的信息查找当前用户的角色或权限(可能需要查询数据库)
Set<String> roles = new HashSet<>();
roles.add("user");
if ("admin".equals(principal)) {
roles.add("admin");
}
// 3. 创建SimpleAuthorizationInfo,并设置roles属性
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
return info;
}
}
github:demo地址