1、什么是Shiro 官网:http://shiro.apache.org/html
Apache Shiro是一个强大易用的Java安全框架,提供了认证、受权、加密和会话管理等功能; 认证 - 用户身份识别,常被称为用户“登陆”; 受权 - 访问控制; 密码加密 - 保护或隐藏数据防止被偷窥; 会话管理 - 每用户相关的时间敏感的状态。 对于任何一个应用程序,Shiro均可以提供全面的安全管理服务。而且相对于其余安全框架,Shiro要简单的多。
2、Shiro的架构介绍 首先,来了解一下Shiro的三个核心组件:Subject, SecurityManager 和 Realms. 以下图: java
Subject:即“当前操做用户”。可是,在Shiro中,Subject这一律念并不只仅指人,也能够是第三方进程、后台账户(Daemon Account)或其余相似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你能够把它认为是Shiro的“用户”概念。 Subject表明了当前用户的安全操做,SecurityManager则管理全部用户的安全操做。web
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro经过SecurityManager来管理内部组件实例,并经过它来提供安全管理的各类服务。算法
Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“链接器”。也就是说,当对用户执行认证(登陆)和受权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。 从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的链接细节,并在须要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)受权。配置多个Realm是能够的,可是至少须要一个。spring
Shiro内置了能够链接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、相似INI的文本配置资源以及属性文件等。若是缺省的Realm不能知足需求,你还能够插入表明自定义数据源的本身的Realm实现。数据库
除前文所讲Subject、SecurityManager 、Realm三个核心组件外,Shiro主要组件还包括:apache
Authenticator :认证就是核实用户身份的过程。这个过程的常见例子是你们都熟悉的“用户/密码”组合。多数用户在登陆软件系统时,一般提供本身的用户名(当事人)和支持他们的密码(证书)。若是存储在系统中的密码(或密码表示)与用户提供的匹配,他们就被认为经过认证。编程
Authorizer :受权实质上就是访问控制 - 控制用户可以访问应用中的哪些内容,好比资源、Web页面等等。缓存
SessionManager :在安全框架领域,Apache Shiro提供了一些独特的东西:可在任何应用或架构层一致地使用Session API。即,Shiro为任何应用提供了一个会话编程范式 - 从小型后台独立应用到大型集群Web应用。这意味着,那些但愿使用会话的应用开发者,没必要被迫使用Servlet或EJB容器了。或者,若是正在使用这些容器,开发者如今也能够选择使用在任何层统一一致的会话API,取代Servlet或EJB机制。安全
CacheManager :对Shiro的其余组件提供缓存支持。
3、代码整合: 1)、pom中加入shiro所需jar ``` <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-aspectj</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-cas</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version> </dependency>
```
2)、web.xml配置shiro拦截器及加载shiro配置文件:
<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:spring-context*.xml classpath:shiro-context.xml </param-value> </context-param> <!-- 确保Web项目中须要权限管理的URL均可以被Shiro拦截过滤 --> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
3)、编写shiro配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd" default-lazy-init="true"> <!-- shiro --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean> <!-- 数据库保存的密码是使用MD5算法加密的,因此这里须要配置一个密码匹配对象 --> <!-- <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.Md5CredentialsMatcher"></bean> --> <!-- 缓存管理 --> <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager"></bean> <!-- 使用Shiro自带的JdbcRealm类 指定密码匹配所须要用到的加密对象 指定存储用户、角色、权限许可的数据源及相关查询语句 --> <!-- <bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm"> <property name="credentialsMatcher" ref="credentialsMatcher"></property> <property name="permissionsLookupEnabled" value="true"></property> <property name="dataSource" ref="dataSource"></property> <property name="authenticationQuery" value="SELECT pswd FROM u_user WHERE mobileId = ?"></property> <property name="userRolesQuery" value="select u_role.name from u_role,u_user_role,u_user where u_role.id = u_user_role.rid and u_user_role.uid = u_user.id and u_user.mobileId = ?"></property> <property name="permissionsQuery" value="select u_permission.* from u_permission,u_role_permission,u_role where u_permission.id=u_role_permission.pid and u_role_permission.rid = u_role.id and u_role.`name` = ?"></property> </bean> --> <!-- Realm实现 --> <bean id="userRealm" class="com.company.project.common.shiro.UserRealm"></bean> <!-- Shiro安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="userRealm"></property> <property name="cacheManager" ref="cacheManager"></property> </bean> <!-- Shiro主过滤器自己功能十分强大,其强大之处就在于它支持任何基于URL路径表达式的、自定义的过滤器的执行 Web应用中,Shiro可控制的Web请求必须通过Shiro主过滤器的拦截,Shiro对基于Spring的Web应用提供了完美的支持 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- Shiro的核心安全接口,这个属性是必须的 --> <property name="securityManager" ref="securityManager"></property> <!-- 要求登陆时的连接(登陆页面地址),非必须的属性,默认会自动寻找Web工程根目录下的"/login.jsp"页面 --> <property name="loginUrl" value="/company/login"></property> <!-- 登陆成功后要跳转的链接(本例中此属性用不到,由于登陆成功后的处理逻辑在LoginController里硬编码) --> <!-- <property name="successUrl" value="/" ></property> --> <!-- 用户访问未对其受权的资源时,所显示的链接 --> <property name="unauthorizedUrl" value="/"></property> <property name="filterChainDefinitions"> <value> /company/login=anon /company/subLogin=anon /company/**=authc </value> </property> </bean> <!-- 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类, 并在必要时进行安全逻辑验证 --> <!-- <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"></property> </bean> --> </beans>
4)、编写自定义Realm实现类,该类继承自AuthorizingRealm,实现其接口
package com.company.project.common.shiro; import java.util.Date; import java.util.Set; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.DisabledAccountException; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.springframework.beans.factory.annotation.Autowired; import com.company.project.common.shiro.token.ShiroToken; import com.company.project.common.utils.Constants; import com.company.project.common.utils.DateUtils; import com.company.project.modules.user.entity.User; import com.company.project.modules.user.service.PermissionService; import com.company.project.modules.user.service.RoleService; import com.company.project.modules.user.service.UserService; /** * 自定义shiro的Realm * @author Sorin * */ public class UserRealm extends AuthorizingRealm{ @Autowired private UserService userService; @Autowired private PermissionService permissionService; @Autowired private RoleService roleService; /** * 该类的做用是为shiro提供认证数据源 */ @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken authcToken) throws AuthenticationException { ShiroToken token = (ShiroToken) authcToken; User user = userService.subLogin(token.getUsername(), token.getPswd()); if(user == null){ throw new UnknownAccountException("账号或密码不正确!");// 没找到账号 }else if(Constants.MobileCode.ISTRUE== user.getStatus()){ throw new DisabledAccountException("账号已经禁止登陆!"); }else{//更新最后登陆时间 user.setLastLoginTime(DateUtils.getFormattedString(new Date())); userService.update(user); } return new SimpleAuthenticationInfo(user.getMobileId(), // 用户名 user.getPswd(), // 密码 getName() // realm name ); } /** * 拼装shiro的受权数据源 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String mobileId = (String)principals.getPrimaryPrincipal(); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); //角色 Set<String> findRoles = roleService.findRoles(mobileId); String roleName = null; for (String role : findRoles) { roleName = role; } authorizationInfo.setRoles(findRoles); //权限 authorizationInfo.setStringPermissions(permissionService.findPermission(roleName)); return authorizationInfo; } }
6)、自定义ShiroToken实体类,继承UsernamePasswordToken
package com.company.project.common.shiro.token; import org.apache.shiro.authc.UsernamePasswordToken; public class ShiroToken extends UsernamePasswordToken implements java.io.Serializable{ private static final long serialVersionUID = -6451794657814516274L; public ShiroToken(String username, String pswd) { super(username,pswd); this.pswd = pswd ; } /** 登陆密码[字符串类型] 由于父类是char[] ] **/ private String pswd ; public String getPswd() { return pswd; } public void setPswd(String pswd) { this.pswd = pswd; } }
7)、TokenManager类,调用shiro认证的service
public static String login(LoginVo user){ ShiroToken token = new ShiroToken(user.getMobileId(), user.getPswd()); token.setRememberMe(user.getIsRememberMe()); SecurityUtils.getSubject().login(token); return getToken(); } public static String getToken(){ return (String)SecurityUtils.getSubject().getPrincipal(); }
8)、LoginController类
//捕捉相应的异常进行处理便可 try{ String mobileId = TokenManager.login(loginVo); }catch (DisabledAccountException e) { e.printStackTrace(); return resultBase("账号已经禁用。", Constants.ResponseResultFlag.ERROR); } catch (Exception e) { e.printStackTrace(); return resultBase("账号或密码错误。", Constants.ResponseResultFlag.ERROR); }
9)、shiro标签使用:
<%--shiro 标签 --%> <%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> .... <%--拥有 角色(管理员) || (用户中心)--%> <shiro:hasAnyRoles name='系统管理员,用户中心'> <shiro:hasPermission name="/member/list.shtml"> <li><a href="/member/list.shtml">用户列表</a></li> </shiro:hasPermission> <shiro:hasPermission name="/member/online.shtml"> <li><a href="/member/online.shtml">在线用户</a></li> </shiro:hasPermission>