# Apache Shiro ## Shiro简介 Shiro是一个简单易用且功能强大的Java安全框架,用于实现认证、授权、加密、session管理等场景,并且Shiro可以被用于任何应用,包括命令行应用、移动应用、大型web应用或是企业应用。 Shiro在如下方面提供Security API: - Authentication:为用户提供身份认证 - Authorization:为应用提供用户的访问控制 - 加密:避免应用数据处于明文状态 - session管理:每个用户的时间敏感状态 ## Shiro中常用的概念 Shiro框架的结构主要分为三个部分:Subject、SecurityManager、Realms ### Subject Subject:Subject是一个安全术语,通常意味着当前执行的用户。 ```java import org.apache.shiro.subject.Subject; import org.apache.shiro.SecurityUtils; // 获取Subject对象 Subject currentUser = SecurityUtils.getSubject(); ``` ### SecurityManager 相对于Subject代表对于当前用户的安全操作,SecurityManager代表对所有用户的安全操作。SecurityManager是Shiro结构的核心,其由多个security component嵌套组成。 > 一旦SecurityManager和其嵌套的security component被配置完成,那么用户将不再使用SecurityManager而是调用Subject API。 对每个应用中,只存在一个SecurityManager,SecurityManager是应用范围内的单例。默认SecurityManager的实现是POJO,可以通过java代码、Spring xml、yaml、properties等方式来进行配置。 ### realms realms是shiro和应用中security data(如账户登录的登录数据或授权管理的权限数据)之间的连接器。当Shiro和security data进行交互时,shiro会从配置好的一个或者多个realm中获取security data。 > 在上述情况下,realm类似于一个安全数据的特定DAO,其封装了到数据源连接的详细信息并且为Shiro提供安全数据。当配置Shiro时,必须指定至少一个Realm用于身份认证和权限认证。 > Shiro提供了开箱即用的Realm来连接很多种security data source,比如LDAP,关系型数据库(JDBC),基于文本配置的data source例如ini或properties文件。可以通过自定义Realm的实现来访问自定义的data source,如果默认的Realm不能满足需求。 ## Authentication 认证过程用于认证用户的身份。认证过程的主要流程如下: 1. 收集用户的身份标识信息(通常称之为主体,principal)和身份证明(通常称之为凭据,credentials) 2. 向系统提交主体和凭据 3. 如果提交的凭据和系统中该主体对应的凭据相同,那么该用户会被认为是“通过认证的”。如果提交的凭据不符,那么该用户会被认为是“认证失败的”。 ```java // 认证流程代码 //1. Acquire submitted principals and credentials: AuthenticationToken token = new UsernamePasswordToken(username, password); //2. Get the current Subject: Subject currentUser = SecurityUtils.getSubject(); //3. Login: currentUser.login(token); ``` 当login方法被调用时,SecurityMananger将会收到AuthenticationToken并且将其分配到一个或多个已经配置好的Realm中,并且让每个Realm执行需要的认证流程。每个Reaml都能对提交的AuthenticaitonToken做出处理。 如果认证过程失败,那么会抛出AuthenticationException,可以通过捕获该异常来对失败的认证进行处理。 ```java //3. Login: try { currentUser.login(token); } catch (IncorrectCredentialsException ice) { … } catch (LockedAccountException lae) { … } … catch (AuthenticationException ae) {… } ``` **当用户通过身份认证之后,其被认为是“通过身份认证的”,并且被允许使用应用。但是,通过身份认证并不意味着可以在系统中执行任何操作。通过授权,可以决定用户能够在系统中执行哪些操作。** ## Authorization Authorization的实质是访问控制,通常用于控制用户在系统中能够使用哪些资源。大多数情况下,可以通过role或permission等形式来实现访问控制,用户通过分配给他们的角色或者权限来执行操作。通过检查用户的role或者permission,系统可以决定将哪些功能暴露给用户。 ```java // 通过如下代码,Subject可以实现对用户的role检测 if ( subject.hasRole(“administrator”) ) { //show the ‘Create User’ button } else { //grey-out the button? } // 通过如下代码,可以实现权限分配而不是角色检测 if ( subject.isPermitted(“user:create”) ) { //show the ‘Create User’ button } else { //grey-out the button? } ``` 权限控制甚至支持非常细粒度的权限,譬如实例级别的权限控制。 ```java // 如下代码检测用户是否拥有删除jsmith用户的权限 if ( subject.isPermitted(“user:delete:jsmith”) ) { //delete the ‘jsmith’ user } else { //don’t delete ‘jsmith’ } ``` 和Authentication类似,Authorization也会进入SecurityManager,并且通过一个或者多个Realm来决定是否允许访问。 **根据需要,Realm既会响应Authentication过程,也会响应Authority过程** ## Session Management Apache提供了一致的会话管理,在任何类型和架构的程序中都可以使用,该Session API从小型的守护进程到大型集群web应用都可以使用。 并且,Shiro提供的Session API是容器无关的,在任何容器环境下Session API都相同。Shiro结构也支持可插入的session存储,可以将session存储在企业缓存、关系型数据库或者nosql中。 Shiro Seesion的另一个好处是Shiro Session可以在不同技术实现的客户端之间进行共享,譬如Swing桌面客户端可以和web客户端一起加入同一个应用会话(用户同时使用swing客户端和web客户端时很有用)。 ```java /** * 用户获取Session */ // 如果当前用户存在会话,则获取已存在会话, // 如果当前用户不存在会话,则创建新的会话 Session session = subject.getSession(); // 接受一个boolean值,该boolean值标识如果当前用户不存在会话,是否创建一个新的会话 Session session = subject.getSession(boolean create); ``` ### Shiro Session可在任何应用中使用 ```java Session session = subject.getSession(); session.getAttribute(“key”, someValue); Date start = session.getStartTimestamp(); Date timestamp = session.getLastAccessTime(); session.setTimeout(millis); ``` ## Shiro加密 Shiro加密是独立于用户(Subject)的,故而Shiro加密可以独立于Subject使用。 Shiro加密主要关注于如下两个模块: - hash加密(又名消息摘要,message digest) - ciphers加密 ### shiro hash 在Shiro hash中,如果想要快速计算文件的MD5值,可以通过如下方式快速计算: ```java String hex = new Md5Hash(myFile).toHex(); ``` 在shiro hash中,如果想要对密码进行sha-512进行hash操作并且对结果进行base64编码成可打印字符串,可以进行如下操作: ```java String encodedPassword = new Sha512Hash(password, salt, count).toBase64(); ``` Shiro框架在很大程度上简化了hash和编码。 ### Shiro Ciphers Ciphers是一种算法,可以通过指定的key将加密后的数据还原到加密之前(不同于hash操作,hash操作通常是不可逆的)。通常用Ciphers来保证数据传输时的安全,防止数据在传输时被窥探。 Shiro通过引入CipherService API来简化加密的流程,**CipherService是一个简单,无状态并且线程安全的API,可以对应用数据进行加密或者解密操作。** ```java // Shiro CipherService对数据进行加密操作 AesCipherService cipherService = new AesCipherService(); cipherService.setKeySize(256); //create a test key: byte[] testKey = cipherService.generateNewKey(); //encrypt a file’s bytes: byte[] encrypted = cipherService.encrypt(fileBytes, testKey); ``` ## Shiro框架的Web支持