Shiro 安全框架十分钟入门指南
当前用户识别
现在我们可以开始探索安全框架的核心功能——执行安全验证操作。
在应用程序安全防护场景中,最常关注的问题是”当前操作用户身份”或”当前用户是否具备执行特定操作的权限”。
在编码开发或界面设计过程中,这些问题频繁出现:应用程序通常基于用户上下文构建,需要根据每个用户的权限范围展示相应功能。因此,基于当前用户进行安全控制是最自然的方式。
Shiro框架通过Subject概念来抽象表示”当前用户”。
在绝大多数运行环境中,可以通过以下方式获取当前执行用户:
Subject currentUser = SecurityUtils.getSubject();
通过SecurityUtils.getSubject()方法,我们能够获取当前执行的Subject实例。Subject是一个安全术语,本质上代表”
当前执行用户的安全视图”。之所以不直接命名为”User”,是因为”User”通常特指人类用户。
在安全领域,”Subject”不仅可以表示人类用户,还可以表示第三方进程、定时任务、守护进程账户等任何与软件交互的实体。
对于大多数应用场景,您可以将Subject理解为Shiro框架中的”用户”概念。
在独立应用程序中调用getSubject()会返回基于特定位置用户数据的Subject,而在服务器环境(如Web应用)中,它会获取基于当前线程或传入请求关联用户数据的Subject。
用户会话管理
获得Subject实例后,我们可以进行哪些操作?
如果需要在应用程序的当前会话中存储用户相关数据,可以获取其会话:
Session session = currentUser.getSession();
session.setAttribute("someKey","aValue");
Session是Shiro特有的会话实例,提供了与HttpSession相似的功能,但具备额外优势且有一个重要区别:它不依赖HTTP环境!
在Web应用程序中,默认Session基于HttpSession实现。但在非Web环境(如本教程的简单应用)中,Shiro会自动启用其企业级会话管理功能。这意味着无论部署环境如何,您都可以在任何应用层使用相同的会话API。这为应用程序开发开辟了新可能,因为需要会话功能的应用程序不再强制依赖HttpSession或有状态会话Bean。同时,各种客户端技术现在能够共享会话数据。
权限验证机制
权限检查只能针对已知身份的用户进行。我们获取的Subject实例代表当前用户,但当前用户具体是谁?在用户完成登录前,他们处于匿名状态。因此,我们需要进行登录验证:
if(!currentUser.isAuthenticated()){
//通过图形界面收集用户凭证信息
//如用户名/密码表单、X509证书、OpenID等
//此处使用最常见的用户名/密码示例
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
//支持"记住我"功能(无需额外配置,内置支持)
token.setRememberMe(true);
currentUser.login(token);
}
实现非常简单!
但如果登录尝试失败如何处理?您可以捕获各种具体异常来了解失败原因,并作出相应处理:
try{
currentUser.login(token);
//无异常抛出表示登录成功
}catch(
UnknownAccountException uae){
//用户名不存在系统,显示错误提示
}catch(
IncorrectCredentialsException ice){
//密码不匹配,请重试
}catch(
LockedAccountException lae){
//账户被锁定,显示提示信息
}
...可检查其他异常类型...
}catch(
AuthenticationException ae){
//意外错误条件处理
}
您可以检查多种异常类型,或根据需求抛出Shiro未提供的自定义异常。请参考AuthenticationException JavaDoc获取详细信息。
实用提示
最安全的做法是向用户显示通用的登录失败信息,避免为潜在攻击者提供系统相关信息。
用户登录成功后,我们还能进行哪些操作?
例如,获取用户身份信息:
//输出用户标识主体(本例中为用户名)
log.info("用户 ["+currentUser.getPrincipal() +"] 登录成功");
检查用户是否拥有特定角色:
if(currentUser.hasRole("schwartz")){
log.
info("愿原力与您同在!");
}else{
log.
info("您好,普通用户");
}
验证用户是否具备对特定类型实体的操作权限:
if(currentUser.isPermitted("lightsaber:weild")){
log.
info("您可以使用光剑戒指,请谨慎使用");
}else{
log.
info("抱歉,光剑戒指仅限原力大师使用");
}
当然,我们还可以执行更精细的实例级权限检查——判断用户是否有权访问特定类型的某个具体实例:
if(currentUser.isPermitted("winnebago:drive:eagle5")){
log.
info("您有权驾驶牌照为'eagle5'的Winnebago,这是钥匙-祝您愉快!");
}else{
log.
info("抱歉,您无权驾驶'eagle5' Winnebago");
}
非常简单,对吧?
最后,当用户结束应用程序使用时,可以执行注销操作:
currentUser.logout(); //移除所有身份信息并使其会话失效
内容总结
希望本教程能帮助您了解如何在基础应用程序中配置Shiro,并理解Shiro的核心设计理念。
