shiro原理的分析,系统权限管理以及 运行流程分析
一、概述
1、shiro是apache的一个开源框架,是一个权限管理的框架,实现 用户认证、用户授权。
2、(1)spring中有spring security (原名Acegi),是一个权限框架,它和spring依赖过于紧密,没有shiro使用简单。
(2) shiro不依赖于spring,shiro不仅可以实现 web应用的权限管理,还可以实现c/s系统,分布式系统权限管理,shiro属于轻量框架,越来越多企业项目开始使用shiro。
(3)使用shiro实现系统的权限管理,有效提高开发效率,从而降低开发成本
二、Shiro架构分析
2、对以上内容解释
subject:主体,可以是用户也可以是程序,主体要访问系统,系统需要对主体进行认证、授权。
securityManager:安全管理器,主体进行认证和授权都是通过securityManager进行。
authenticator:认证器,主体进行认证最终通过authenticator进行的。
authorizer:授权器,主体进行授权最终通过authorizer进行的。
sessionManager:web应用中一般是用web容器对session进行管理,shiro也提供一套session管理的方式。
SessionDao: 通过SessionDao管理session数据,针对个性化的session数据存储需要使用sessionDao。
cache Manager:缓存管理器,主要对session和授权数据进行缓存,比如将授权数据通过cacheManager进行缓存管理,和ehcache整合对缓存数据进行管理。
realm:域,领域,相当于数据源,通过realm存取认证、授权相关数据。
注意:在realm中存储授权和认证的逻辑。
cryptography:密码管理,提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。
比如 md5散列算法。
三、认证过程
认证执行流程
1、通过ini配置文件创建securityManager
2、调用subject.login方法主体提交认证,提交的token
3、securityManager进行认证,securityManager最终由ModularRealmAuthenticator进行认证。
4、ModularRealmAuthenticator调用IniRealm(给realm传入token) 去ini配置文件中查询用户信息
5、IniRealm根据输入的token(UsernamePasswordToken)从 shiro.ini查询用户信息,根据账号查询用户信息(账号和密码)
如果查询到用户信息,就给ModularRealmAuthenticator返回用户信息(账号和密码)
如果查询不到,就给ModularRealmAuthenticator返回null
6、ModularRealmAuthenticator接收IniRealm返回Authentication认证信息
如果返回的认证信息是null,ModularRealmAuthenticator抛出异常(org.apache.shiro.authc.UnknownAccountException)
如果返回的认证信息不是null(说明inirealm找到了用户),对IniRealm返回用户密码 (在ini文件中存在)和 token中的密码 进行对比,如果不一致抛出异常(org.apache.shiro.authc.IncorrectCredentialsException)
授权流程:
1、对subject进行授权,调用方法isPermitted("permission串")
2、SecurityManager执行授权,通过ModularRealmAuthorizer执行授权
3、ModularRealmAuthorizer执行realm(自定义的Realm)从数据库查询权限数据
调用realm的授权方法:doGetAuthorizationInfo
4、realm从数据库查询权限数据,返回ModularRealmAuthorizer
5、ModularRealmAuthorizer调用PermissionResolver进行权限串比对
6、如果比对后,isPermitted中"permission串"在realm查询到权限数据中,说明用户访问permission串有权限,否则 没有权限,抛出异常。
详细概述Shiro分析介绍
1.简介
常用的Java EE安全框架有shiro、spring security。shiro被应用非常广泛,可以集成cas,搭建单点登录系统。spring security则被认为比较重,应用没有shiro广泛。shiro提供用户名、密码验证,及密码的加密存储,会话Session的管理,与web集成,支持HTTPS的拦截。
2.Shiro原理简析
shiro原理剖析:
shiro的核心是javaservlet规范中的filter,通过配置拦截器,使用拦截器链来拦截请求,如果允许访问,则通过。通常情况下,系统的登录、退出会配置拦截器。登录的时候,调用subject.login(token),token是用户验证信息,这个时候会在Realm中doGetAuthenticationInfo方法中进行认证。这个时候会把用户提交的验证信息与数据库中存储的认证信息进行比较,一致则允许访问,并在浏览器种下此次回话的cookie,在服务器端存储session信息。退出的时候,调用subject.logout(),会清除回话信息。
shiro中核心概念介绍:
Filter:
1.AnonymousFilter:通过此filter修饰的url,任何人都可以进行访问,即使没有进行权限认证
2.FormAuthenticationFilter:通过此filter修饰的url,会对请求的url进行验证,如果没有通过,则会重定向返回到loginurl
3.BasicHttpAuthenticationFilter:通过此filter修饰的url,要求用户已经通过认证,如果没有通过,则会要求通过Authorization 信息进行认证
4.LogoutFilter:通过此filter修饰的url,一旦收到url请求,则会立即调用subject进行退出,并重定向到redirectUrl
5.NoSessionCreationFilter:通过此filter修饰的url,不会创建任何会话
6.PermissionAuthorizationFilter:权限拦截器,验证用户是否具有相关权限
7.PortFilter:端口拦截器,不是通过制定端口访问url,将自动将端口重定向到指定端口
8.HttpMethodPermissionFilter:rest风格拦截器,配置rest的访问方式
9.RolesAuthorizationFilter:角色拦截器,未登陆,将跳转到loginurl,未授权,将跳转到unauthorizedUrl
10.SslFilter:HTTPS拦截器,需要以HTTPS的方式进行访问
11.UserFilter:用户拦截器,需要用户已经认证,或已经remember me
Subject:
表示当前操作的主体用户,是一个抽象的概念,Subject可以进行登录、退出、权限判断等动作,通常一个subject与一个线程进行关联。
Realm:
表示验证的数据源,存储用户的安全数据,可以进行用户名和密码的匹配,及用户权限查询。
3.Shiro配置方法
.
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-4.0.xsd
- http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-4.0.xsd
- http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
- <description>Apache Shiro Security Configuration</description>
- <!-- Realm实现 -->
- <bean id="userRealm" class="com.ttyc.mammon.service.shiro.UserRealm">
- <property name="cachingEnabled" value="false"/>
- </bean>
- <!-- 会话ID生成器 -->
- <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
- <!-- 会话Cookie模板 -->
- <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
- <constructor-arg value="sid"/>
- <property name="domain" value=".ttyongche.com"/>
- <property name="path" value="/"/>
- <property name="httpOnly" value="false"/>
- <property name="maxAge" value="-1"/>
- </bean>
- <!-- 会话DAO -->
- <bean id="sessionDAO" class="org.crazycake.shiro.RedisSessionDAO">
- <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
- <property name="redisManager" ref="redisManager"/>
- </bean>
- <!-- 会话管理器 -->
- <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
- <property name="globalSessionTimeout" value="1800000"/>
- <property name="deleteInvalidSessions" value="true"/>
- <property name="sessionDAO" ref="sessionDAO"/>
- <property name="sessionIdCookieEnabled" value="true"/>
- <property name="sessionIdCookie" ref="sessionIdCookie"/>
- </bean>
- <!-- 安全管理器 -->
- <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
- <property name="realm" ref="userRealm"/>
- <property name="sessionManager" ref="sessionManager"/>
- <property name="cacheManager" ref="redisCacheManager"/>
- </bean>
- <!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
- <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
- <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
- <property name="arguments" ref="securityManager"/>
- </bean>
- <bean id="authFilter" class="com.ttyc.mammon.controller.filter.AuthFilter"/>
- <bean id="permissionFilter" class="com.ttyc.mammon.controller.filter.PermissionFilter"/>
- <!-- Shiro的Web过滤器 -->
- <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
- <property name="securityManager" ref="securityManager"/>
- <property name="loginUrl" value="/login"/>
- <property name="filters">
- <util:map>
- <entry key="auth" value-ref="authFilter"/>
- <entry key="perm" value-ref="permissionFilter"/>
- </util:map>
- </property>
- <property name="filterChainDefinitions">
- <value>
- /** = auth
- </value>
- </property>
- </bean>
- <!-- Shiro生命周期处理器-->
- <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
- <!-- cacheManager -->
- <bean id="redisCacheManager" class="org.crazycake.shiro.RedisCacheManager">
- <property name="redisManager" ref="redisManager" />
- </bean>
- <!-- shiro redisManager -->
- <bean id="redisManager" class="com.ttyc.mammon.service.shiro.RedisManager">
- <property name="host" value="${redis.host}" />
- <property name="port" value="${redis.port}" />
- <property name="expire" value="${redis.expire}" />
- <property name="dataBase" value="${redis.database}" />
- </bean>
- </beans>
30. Pom依赖:
31. <shiro.version>1.2.2</shiro.version>
32. <shiro.redis.version>2.4.2.1-RELEASE</shiro.redis.version>
33. [html] view plain copy
- <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-spring</artifactId>
- <version>${shiro.version}</version>
- </dependency>
- <dependency>
- <groupId>org.crazycake</groupId>
- <artifactId>shiro-redis</artifactId>
- <version>${shiro.redis.version}</version>
- </dependency>
34. UserRealm:
35. 继承AuthorizingRealm,重写doGetAuthorization和doGetAuthorication方法。
36. [java] view plain copy
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
- String username = (String)principals.getPrimaryPrincipal();
- SysUser user = getUserByName(username);
- SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
- SysRole sysRole = sysRolePermService.getSysRoleByUserId(user.getId());
- addRole(authorizationInfo,sysRole);
- List<SysPermission> sysPermissions = sysRolePermService.getSysPermissionByUserId(user.getId());
- addPermissions(authorizationInfo,sysPermissions);
- return authorizationInfo;
- }
- @Override
- protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
- String username = (String)token.getPrincipal();
- SysUser user = getUserByName(username);
- //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以自定义实现
- if (user != null){
- return new SimpleAuthenticationInfo(user.getUserName(),"", getName());
- }
- return null;
- }
37. AuthFilter:
38. AuthFilter继承AccessControllerFilter,需要重写isAccessAllowed和onAccessDenied方法
39.
40. [java] view plain copy
- @Override
- protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
- HttpServletRequest httpRequest = (HttpServletRequest)request;
- HttpServletResponse httpResponse = (HttpServletResponse)response;
- //支持跨域
- supportCrossDomain(httpResponse);
- // 对外API,通过Service-Token进行认证
- String url = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length());
- if (url.startsWith(HTTP_API_URL)) {
- if (validateSign(httpRequest.getHeader("Service-Token"))) {
- return true;
- }
- else {
- return false;
- }
- }
- //运营后台通过cookie,token,sid进行认证
- Subject currentSubject = SecurityUtils.getSubject();
- if (! currentSubject.isAuthenticated()){
- SysUser sysTokenUser = loginAuthService.validTokenAuth((HttpServletRequest) request);
- SysUser sysCookieUser = loginAuthService.validCookieAuth((HttpServletRequest) request);
- if (sysTokenUser == null && sysCookieUser == null){
- return false;
- }
- SysUser sysUser = null;
- if (sysTokenUser != null){
- sysUser = sysTokenUser;
- }else {
- sysUser = sysCookieUser;
- }
- UsernamePasswordToken token = new UsernamePasswordToken(sysUser.getUserName(),"");
- currentSubject.login(token);
- Session session = currentSubject.getSession();
- session.setAttribute("LoginUser",sysUser);
- }
- //在request里设置登录用户
- SysUser currentUser = (SysUser) currentSubject.getSession().getAttribute("LoginUser");
- request.setAttribute("loginId", currentUser.getId());
- return true;
- }
- @Override
- protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
- response.setContentType("application/json; charset=utf-8");
- response.getWriter().write(json);
- return false;
- }
41.
42. RedisManager:
43. shiro原生不支持redis,只支持ehcache,ehcache只能单机缓存,不适合于大型集群应用。第三方crazycake支持shiro使用redis缓存,配置方式如下:
44.
45. [java] view plain copy
- import redis.clients.jedis.Jedis;
- import redis.clients.jedis.JedisPool;
- import redis.clients.jedis.JedisPoolConfig;
- import java.util.Set;
- public class RedisManager extends org.crazycake.shiro.RedisManager {
- private String host = "127.0.0.1";
- private int port = 6379;
- // 0 - never expire
- private int expire = 0;
- // timeout for jedis try to connect to redis server, not expire time! In
- // milliseconds
- private int timeout = 0;
- private String password = "";
- private int dataBase;
- private static JedisPool jedisPool = null;
- public RedisManager() {
- }
- /**
- * 初始化方法
- */
- public void init() {
- if (jedisPool == null) {
- if (password != null && "".equals(password.trim())) {
- password = null;
- }
- jedisPool = new JedisPool(new JedisPoolConfig(), host, port, timeout, password, dataBase);
- }
- }
- /**
- * get value from redis
- *
- * @param key
- * @return
- */
- public byte[] get(byte[] key) {
- byte[] value = null;
- Jedis jedis = jedisPool.getResource();
- try {
- value = jedis.get(key);
- } finally {
- jedis.close();
- }
- return value;
- }
- /**
- * set
- *
- * @param key
- * @param value
- * @return
- */
- public byte[] set(byte[] key, byte[] value) {
- Jedis jedis = jedisPool.getResource();
- try {
- jedis.set(key, value);
- if (this.expire != 0) {
- jedis.expire(key, this.expire);
- }
- } finally {
- jedis.close();
- }
- return value;
- }
- /**
- * set
- *
- * @param key
- * @param value
- * @param expire
- * @return
- */
- public byte[] set(byte[] key, byte[] value, int expire) {
- Jedis jedis = jedisPool.getResource();
- try {
- jedis.set(key, value);
- if (expire != 0) {
- jedis.expire(key, expire);
- }
- } finally {
- jedis.close();
- }
- return value;
- }
- /**
- * del
- *
- * @param key
- */
- public void del(byte[] key) {
- Jedis jedis = jedisPool.getResource();
- try {
- jedis.del(key);
- } finally {
- jedis.close();
- }
- }
- /**
- * flush
- */
- public void flushDB() {
- Jedis jedis = jedisPool.getResource();
- try {
- jedis.flushDB();
- } finally {
- jedis.close();
- }
- }
- /**
- * size
- */
- public Long dbSize() {
- Long dbSize = 0L;
- Jedis jedis = jedisPool.getResource();
- try {
- dbSize = jedis.dbSize();
- } finally {
- jedis.close();
- }
- return dbSize;
- }
- /**
- * keys
- *
- * @param pattern
- * @return
- */
- public Set<byte[]> keys(String pattern) {
- Set<byte[]> keys = null;
- Jedis jedis = jedisPool.getResource();
- try {
- keys = jedis.keys(pattern.getBytes());
- } finally {
- jedis.close();
- }
- return keys;
- }
- public String getHost() {
- return host;
- }
- public void setHost(String host) {
- this.host = host;
- }
- public int getPort() {
- return port;
- }
- public void setPort(int port) {
- this.port = port;
- }
- public int getExpire() {
- return expire;
- }
- public void setExpire(int expire) {
- this.expire = expire;
- }
- public int getTimeout() {
- return timeout;
- }
- public void setTimeout(int timeout) {
- this.timeout = timeout;
- }
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- public int getDataBase() {
- return dataBase;
- }
- public void setDataBase(int dataBase) {
- this.dataBase = dataBase;
- }
- }