Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、加密、会话管理、与 Web 集成、缓存等。相当简单,对比 Spring Security,可能没有 Spring Security 做的功能强大。Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。
功能点:
Authentication:身份认证 / 登录,验证用户是不是拥有相应的身份;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Web Support:Web 支持,可以非常容易的集成到 Web 环境;
Caching:缓存,比如用户登录后,其用户信息、拥有的角色 / 权限不必每次去查,这样可以提高效率;
Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。Subject代表了当前用户的安全操作,所有 Subject 都绑定到 SecurityManager,SecurityManager则管理所有用户的安全操作。
SecurityManager:安全管理器,它是Shiro框架的核心,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
在 shiro 中,用户需要提供 principals (身份)和 credentials(证明)给 shiro,从而应用能验证用户身份:
shrio.ini
[users]#用户=密码,角色
root = secret, admin
guest = guest, guest
presidentskroob = 12345, president
darkhelmet = ludicrousspeed, darklord, schwartz
lonestarr = vespa, goodguy, schwartz
[roles]#角色=权限(角色具有的权限)
admin = *
schwartz = lightsaber:*
goodguy = user:delete:zhangsan
public class Quickstart {
private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
public static void main(String[] args) {
//创建shrio核心 securityManager
Factory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
//创建subject
Subject currentUser = SecurityUtils.getSubject();
//获取session
Session session = currentUser.getSession();
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("---> Retrieved the correct value! [" + value + "]");
}
//测试当前用户是否已经认证,即是否登录
if (!currentUser.isAuthenticated()) {
// 将用户名和密码封装为 UsernamePasswordToken
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
// rememberme:免登录也可访问,authc的资源必须登录才行
token.setRememberMe(true);
try {
//执行登录 即后面的realm的认证方法 将token给realm protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException{}方法
currentUser.login(token);
}
// 未知的账户
catch (UnknownAccountException uae) {
log.info("----> There is no user with username of " + token.getPrincipal());
return;
}
// 密码不正确
catch (IncorrectCredentialsException ice) {
log.info("----> Password for account " + token.getPrincipal() + " was incorrect!");
return;
}
//账户呗锁定
catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
}
//
catch (AuthenticationException ae) {
//unexpected condition? error?
}
}
log.info("----> User [" + currentUser.getPrincipal() + "] logged in successfully.");
// 是否有schwartz的角色
if (currentUser.hasRole("schwartz")) {
log.info("----> May the Schwartz be with you!");
} else {
log.info("----> Hello, mere mortal.");
return;
}
//是否有lightsaber:weild的权限(测试用户是否有什么行为)
if (currentUser.isPermitted("lightsaber:weild")) {
log.info("----> You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
//
if (currentUser.isPermitted("user:delete:zhangsan")) {
log.info("----> You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
"Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}
//登出
System.out.println("---->" + currentUser.isAuthenticated());
currentUser.logout();
System.out.println("---->" + currentUser.isAuthenticated());
System.exit(0);
}
}