你的位置:首页 > Java教程

[Java教程]Shiro 整合SpringMVC 并且实现权限管理,登录和注销


Apache Shiro是Java的一个安全框架。目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。

  因为我总结的是使用SpringMVC和Apache Shiro整合,注重的是整合和使用,至于基础,我这里就不细说了.按照惯例,既然是需要创建项目,那么我们首先需要JAR包,Apache shiro的架包除了除了基本的以外,我们还需要shiro-web和shiro-spring的的架包,下面是所需要的所有shiro架包,至于其他的架包,像缓存的架包,Spring和SpringMVC的架包还是和我们以前使用的架包一样的。

<dependency>     <groupId>org.apache.shiro</groupId>     <artifactId>shiro-core</artifactId>     <version>1.2.3</version>   </dependency>  <dependency>    <groupId>org.apache.shiro</groupId>    <artifactId>shiro-ehcache</artifactId>    <version>1.2.3</version>  </dependency>  <dependency>    <groupId>org.apache.shiro</groupId>    <artifactId>shiro-web</artifactId>    <version>1.2.3</version>  </dependency>  <dependency>    <groupId>org.apache.shiro</groupId>    <artifactId>shiro-spring</artifactId>    <version>1.2.3</version>  </dependency>

  所有的架包都搞清楚了以后,我们就可以开始正式搭建了,在myeclise中创建一个maven项目,将需要的架包信息依赖全部放入。下面就分步骤来创建
  1.首先创建spring的配置文件,位置都在在resource中,配置文件是spring-context.  

 <!-- 配置spring容器的路径 --> <context-param>     <param-name>contextConfigLocation</param-name>     <param-value>classpath*:/spring-context-*.</param-value> </context-param> <!-- 对spring开始监听 --> <listener>   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>

这样就可以扫描到两个配置文件了,又不会扫描到我们的spring-mvc.

2除了在web.

<!-- MVC Servlet   设置springmvc的Servlet   --> <servlet>   <servlet-name>springServlet</servlet-name>   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>   <init-param>     <param-name>contextConfigLocation</param-name>     <param-value>classpath:springmvc.</param-value>   </init-param>   <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping>   <servlet-name>springServlet</servlet-name>   <url-pattern>/</url-pattern> </servlet-mapping>

3.在web.

   <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>

注意,这个shiroFilter名称,后面的配置还需要使用到,所以要注意咯。
4,因为shiro的session是自己实现的,所以我们还需要一个缓存框架,所以在spring的配置文件一定要注意配置哦,

  <!-- 缓存 -->  <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">    <property name="configLocation" value="classpath:${ehcache.file}"></property>  </bean>

spring的其他的配置,该怎样还是这样,我们的重点是配置spring-context-shiro.

  

<??><beans ="http://www.springframework.org/schema/beans" ="http://www.w3.org/2001/  ="http://www.springframework.org/schema/context" xsi:schemaLocation="    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"  default-lazy-init="true">  <description>Shiro Configuration</description>  <!-- 加载配置属性文件 -->  <context:property-placeholder ignore-unresolvable="true" location="classpath:yonyou.properties" />    <!-- Shiro权限过滤过滤器定义 -->  <bean name="shiroFilterChainDefinitions" class="java.lang.String">    <constructor-arg>      <value>        /static/** = anon        /userfiles/** = anon        ${adminPath}/cas = cas        ${adminPath}/login = authc        ${adminPath}/logout = logout        ${adminPath}/** = user      </value>    </constructor-arg>  </bean>    <!-- 安全认证过滤器 -->  <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">    <property name="securityManager" ref="securityManager" /><!--     <property name="loginUrl" value="${cas.server.url}?service=${cas.project.url}${adminPath}/cas" /> -->    <property name="loginUrl" value="${adminPath}/login" />    <property name="successUrl" value="${adminPath}?login" />    <property name="filters">      <map>        <entry key="cas" value-ref="casFilter"/>        <entry key="authc" value-ref="formAuthenticationFilter"/>      </map>    </property>    <property name="filterChainDefinitions">      <ref bean="shiroFilterChainDefinitions"/>    </property>  </bean>    <!-- CAS认证过滤器 -->   <bean id="casFilter" class="org.apache.shiro.cas.CasFilter">     <property name="failureUrl" value="${adminPath}/login"/>  </bean>    <!-- 定义Shiro安全管理配置 -->  <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">    <property name="realm" ref="systemAuthorizingRealm" />    <property name="sessionManager" ref="sessionManager" />    <property name="cacheManager" ref="shiroCacheManager" />  </bean>    <!-- 自定义会话管理配置 -->  <bean id="sessionManager" class="com.yonyou.hotusm.common.security.session.SessionManager">     <property name="sessionDAO" ref="sessionDAO"/>        <!-- 会话超时时间,单位:毫秒 -->    <property name="globalSessionTimeout" value="${session.sessionTimeout}"/>        <!-- 定时清理失效会话, 清理用户直接关闭浏览器造成的孤立会话  -->    <property name="sessionValidationInterval" value="${session.sessionTimeoutClean}"/><!--     <property name="sessionValidationSchedulerEnabled" value="false"/> -->     <property name="sessionValidationSchedulerEnabled" value="true"/>         <property name="sessionIdCookie" ref="sessionIdCookie"/>    <property name="sessionIdCookieEnabled" value="true"/>  </bean>    <!-- 指定本系统SESSIONID, 默认为: JSESSIONID 问题: 与SERVLET容器名冲突, 如JETTY, TOMCAT 等默认JSESSIONID,    当跳出SHIRO SERVLET时如ERROR-PAGE容器会为JSESSIONID重新分配值导致登录会话丢失! -->  <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">    <constructor-arg name="name" value="jeesite.session.id"/>  </bean>  <!-- 自定义Session存储容器 --><!--   <bean id="sessionDAO" > --><!--     <property name="sessionIdGenerator" ref="idGen" /> --><!--     <property name="sessionKeyPrefix" value="${redis.keyPrefix}_session_" /> --><!--   </bean> -->  <bean id="sessionDAO" class="com.yonyou.hotusm.common.security.session.CacheSessionDAO">    <property name="sessionIdGenerator" ref="idGen" />    <property name="activeSessionsCacheName" value="activeSessionsCache" />    <property name="cacheManager" ref="shiroCacheManager" />  </bean>    <!-- 定义授权缓存管理器 --><!--   <bean id="shiroCacheManager" /> -->  <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">    <property name="cacheManager" ref="cacheManager"/>  </bean>    <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->  <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>    <!-- AOP式方法级权限检查 -->  <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">    <property name="proxyTargetClass" value="true" />  </bean>  <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">    <property name="securityManager" ref="securityManager"/>  </bean>  </beans>

 

ecurityManager:是shiro最重要的一个对象,授权和验证都是由它来做的,下面就一一的来讲他的依赖类,

一:realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。下对于源代码,我就不细细的研究了,下面是我重写的realm,:

package com.yonyou.hotusm.module.sys.security; import java.io.Serializable; import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.SimpleAuthenticationInfo;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.authz.AuthorizationInfo;import org.apache.shiro.authz.SimpleAuthorizationInfo;import org.apache.shiro.authz.UnauthenticatedException;import org.apache.shiro.realm.AuthorizingRealm;import org.apache.shiro.subject.PrincipalCollection;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service; import com.yonyou.hotusm.module.sys.dao.UserDao;import com.yonyou.hotusm.module.sys.entity.User;import com.yonyou.hotusm.module.sys.util.UserUtils; @Servicepublic class SystemAuthorizingRealm extends AuthorizingRealm{ @Autowiredprivate UserDao userDao;@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();info.addStringPermission("sys:manager");info.addStringPermission("user");System.out.println("开始授权");return info;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {UsernamePasswordToken upToken=(UsernamePasswordToken) token; String username=upToken.getUsername();String password=new String(upToken.getPassword());User user=new User();user.setLoginName(username);user=userDao.get(user);System.out.println("===========");if(user!=null){if(user.getPassword().equals(password)){return new SimpleAuthenticationInfo(username,password,getName());}}throw new UnauthenticatedException(); }public static class Principal implements Serializable {private static final long serialVersionUID = 1L;private String id; // 编号private String loginName; // 登录名private String name; // 姓名 public Principal(User user) {this.id = user.getId();this.loginName = user.getLoginName();this.name = user.getName();}public String getId() {return id;}public String getLoginName() {return loginName;} public String getName() {return name;} /** * 获取SESSIONID */public String getSessionid() {try{return (String) UserUtils.getSession().getId();}catch (Exception e) {return "";}}@Overridepublic String toString() {return id;}}}

 

 

看的出来,其中最重要的是doGetAuthorizationInfo和doGetAuthenticationInfo,这两个方法,doGetAuthorizationInfo是对当前的用户进行授权的,至于授权的时期,就是当用户需要验证的时候,我这里只是简单的写死了,但是在实际项目开发中,我们一般会将权限存放在数据表中,所以真实情况是先到数据库中查出一个集合,然后迭代授权,

  doGetAuthenticationInfo对于的是对用户验证,这里我们就需要从数据库中根据用户查出用户,根据用户情况,抛出不用的异常。

下面就是讲解sessionManager,因为Shiro有自己的一套session体系,有sessionManager就不奇怪了,sessionManager主要职责是管理session的创建和删除,特别提一下,sessionManager对session的操作,其实只是调用了sessionDAO,然再加上自己的一些操作,所以,我们可以看到sessionManager的bean还依赖sessionDAO,下面是自己实现的sessionManager:

package com.yonyou.hotsum.common.security.shiro.session; import java.io.Serializable;import java.util.Collection;import java.util.Date; import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse; import org.apache.shiro.session.InvalidSessionException;import org.apache.shiro.session.Session;import org.apache.shiro.session.UnknownSessionException;import org.apache.shiro.session.mgt.SessionContext;import org.apache.shiro.session.mgt.SessionKey;import org.apache.shiro.session.mgt.SimpleSession;import org.apache.shiro.web.servlet.Cookie;import org.apache.shiro.web.servlet.ShiroHttpServletRequest;import org.apache.shiro.web.servlet.SimpleCookie;import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;import org.apache.shiro.web.util.WebUtils; /** * 自定义WEB会话管理类 * @author hotusm * */public class SessionManager extends DefaultWebSessionManager { public SessionManager() {super();} @Overrideprotected Serializable getSessionId(ServletRequest request, ServletResponse response) {// 如果参数中包含“__sid”参数,则使用此sid会话。 例如:http://localhost/project?__sid=xxx&__cookie=trueString sid = request.getParameter("__sid");if (org.apache.commons.lang3.StringUtils.isNotBlank(sid)) {// 是否将sid保存到cookie,浏览器模式下使用此参数。if (WebUtils.isTrue(request, "__cookie")){    HttpServletRequest rq = (HttpServletRequest)request;    HttpServletResponse rs = (HttpServletResponse)response;Cookie template = getSessionIdCookie();    Cookie cookie = new SimpleCookie(template);cookie.setValue(sid); cookie.saveTo(rq, rs);}// 设置当前session状态      request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,          ShiroHttpServletRequest.URL_SESSION_ID_SOURCE); // session来源与url      request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sid);      request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);     return sid;}else{return super.getSessionId(request, response);}} @Overridepublic void validateSessions() {super.validateSessions();} @Overrideprotected Session retrieveSession(SessionKey sessionKey) {try{return super.retrieveSession(sessionKey);}catch (UnknownSessionException e) {   // 获取不到SESSION不抛出异常return null;}}   @Overridepublic Date getStartTimestamp(SessionKey key) {   try{   return super.getStartTimestamp(key);   }catch (InvalidSessionException e) {   // 获取不到SESSION不抛出异常     return null;}  }   @Overridepublic Date getLastAccessTime(SessionKey key) {   try{   return super.getLastAccessTime(key);   }catch (InvalidSessionException e) {   // 获取不到SESSION不抛出异常     return null;}  }   @Overridepublic long getTimeout(SessionKey key){   try{   return super.getTimeout(key);   }catch (InvalidSessionException e) {   // 获取不到SESSION不抛出异常     return 0;}  }   @Overridepublic void setTimeout(SessionKey key, long maxIdleTimeInMillis) {   try{   super.setTimeout(key, maxIdleTimeInMillis);   }catch (InvalidSessionException e) {   // 获取不到SESSION不抛出异常}  }   @Overridepublic void touch(SessionKey key) {   try{   super.touch(key);}catch (InvalidSessionException e) {// 获取不到SESSION不抛出异常}  }   @Overridepublic String getHost(SessionKey key) {   try{   return super.getHost(key);   }catch (InvalidSessionException e) {   // 获取不到SESSION不抛出异常     return null;}  }   @Overridepublic Collection<Object> getAttributeKeys(SessionKey key) {   try{   return super.getAttributeKeys(key);   }catch (InvalidSessionException e) {   // 获取不到SESSION不抛出异常     return null;}  }   @Overridepublic Object getAttribute(SessionKey sessionKey, Object attributeKey) {   try{   return super.getAttribute(sessionKey, attributeKey);   }catch (InvalidSessionException e) {   // 获取不到SESSION不抛出异常     return null;}  }   @Overridepublic void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) {   try{   super.setAttribute(sessionKey, attributeKey, value);   }catch (InvalidSessionException e) {   // 获取不到SESSION不抛出异常}  }   @Overridepublic Object removeAttribute(SessionKey sessionKey, Object attributeKey) {   try{   return super.removeAttribute(sessionKey, attributeKey);   }catch (InvalidSessionException e) {   // 获取不到SESSION不抛出异常     return null;}  }   @Overridepublic void stop(SessionKey key) {   try{   super.stop(key);   }catch (InvalidSessionException e) {   // 获取不到SESSION不抛出异常}  }    @Overridepublic void checkValid(SessionKey key) {   try{   super.checkValid(key);}catch (InvalidSessionException e) {// 获取不到SESSION不抛出异常}  }    @Override  protected Session doCreateSession(SessionContext context) {   try{   return super.doCreateSession(context);}catch (IllegalStateException e) {return null;}  } @Overrideprotected Session newSessionInstance(SessionContext context) {Session session = super.newSessionInstance(context);session.setTimeout(getGlobalSessionTimeout());return session;}    @Override  public Session start(SessionContext context) {   try{   return super.start(context);}catch (NullPointerException e) {SimpleSession session = new SimpleSession();session.setId(0);return session;}  }}

 

看代码就明白,其实就是对session的操作,

还有就是sessionDAO了,这个sessionDAO才是真正对session操作的bean:

 

package com.yonyou.hotusm.common.security.shiro.session; import java.io.Serializable;import java.util.Collection;import java.util.Set; import javax.servlet.http.HttpServletRequest; import org.apache.shiro.session.Session;import org.apache.shiro.session.UnknownSessionException;import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;import org.apache.shiro.subject.PrincipalCollection;import org.apache.shiro.subject.support.DefaultSubjectContext;import org.slf4j.Logger;import org.slf4j.LoggerFactory; import com.google.common.collect.Sets; import com.yonyou.hotusm.common.config.Global;import com.yonyou.hotusm.common.utils.DateUtils;import com.yonyou.hotusm.common.web.Servlets; /** * 系统安全认证实现类 * @author hotusm * */public class CacheSessionDAO extends EnterpriseCacheSessionDAO implements SessionDAO { private Logger logger = LoggerFactory.getLogger(getClass());   public CacheSessionDAO() {    super();  }   @Override  protected void doUpdate(Session session) {   if (session == null || session.getId() == null) {       return;    }     HttpServletRequest request = Servlets.getRequest();if (request != null){String uri = request.getServletPath();// 如果是静态文件,则不更新SESSIONif (Servlets.isStaticFile(uri)){return;}// 如果是视图文件,则不更新SESSIONif (org.apache.commons.lang3.StringUtils.startsWith(uri, Global.getConfig("web.view.prefix"))&& org.apache.commons.lang3.StringUtils.endsWith(uri, Global.getConfig("web.view.suffix"))){return;}// 手动控制不更新SESSIONString updateSession = request.getParameter("updateSession");if (Global.FALSE.equals(updateSession) || Global.NO.equals(updateSession)){return;}}   super.doUpdate(session);   logger.debug("update {} {}", session.getId(), request != null ? request.getRequestURI() : "");  }   @Override  protected void doDelete(Session session) {   if (session == null || session.getId() == null) {       return;    }     super.doDelete(session);   logger.debug("delete {} ", session.getId());  }   @Override  protected Serializable doCreate(Session session) {HttpServletRequest request = Servlets.getRequest();if (request != null){String uri = request.getServletPath();// 如果是静态文件,则不创建SESSIONif (Servlets.isStaticFile(uri)){    return null;}}super.doCreate(session);logger.debug("doCreate {} {}", session, request != null ? request.getRequestURI() : "");   return session.getId();  }   @Override  protected Session doReadSession(Serializable sessionId) {return super.doReadSession(sessionId);  }    @Override  public Session readSession(Serializable sessionId) throws UnknownSessionException {   try{   Session s = null;   HttpServletRequest request = Servlets.getRequest();   if (request != null){   String uri = request.getServletPath();   // 如果是静态文件,则不获取SESSION   if (Servlets.isStaticFile(uri)){   return null;   }   s = (Session)request.getAttribute("session_"+sessionId);   }   if (s != null){   return s;   }   Session session = super.readSession(sessionId);   logger.debug("readSession {} {}", sessionId, request != null ? request.getRequestURI() : "");     if (request != null && session != null){   request.setAttribute("session_"+sessionId, session);   }     return session;   }catch (UnknownSessionException e) {return null;}  }   /** * 获取活动会话 * @param includeLeave 是否包括离线(最后访问时间大于3分钟为离线会话) * @return */@Overridepublic Collection<Session> getActiveSessions(boolean includeLeave) {return getActiveSessions(includeLeave, null, null);}    /** * 获取活动会话 * @param includeLeave 是否包括离线(最后访问时间大于3分钟为离线会话) * @param principal 根据登录者对象获取活动会话 * @param filterSession 不为空,则过滤掉(不包含)这个会话。 * @return */@Overridepublic Collection<Session> getActiveSessions(boolean includeLeave, Object principal, Session filterSession) {// 如果包括离线,并无登录者条件。if (includeLeave && principal == null){return getActiveSessions();}Set<Session> sessions = Sets.newHashSet();for (Session session : getActiveSessions()){boolean isActiveSession = false;// 不包括离线并符合最后访问时间小于等于3分钟条件。if (includeLeave || DateUtils.pastMinutes(session.getLastAccessTime()) <= 3){isActiveSession = true;}// 符合登陆者条件。if (principal != null){PrincipalCollection pc = (PrincipalCollection)session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);if (principal.toString().equals(pc != null ? pc.getPrimaryPrincipal().toString() : org.apache.commons.lang3.StringUtils.EMPTY)){isActiveSession = true;}}// 过滤掉的SESSIONif (filterSession != null && filterSession.getId().equals(session.getId())){isActiveSession = false;}if (isActiveSession){sessions.add(session);}}return sessions;}}

,看sessionDAO还有一个idGen依赖bean,指的是id的生成策略,这个bean也是自己定义的,但是需要继承SessionIdGenerator,其中就有public Serializable generateId(Session session),返回的就是session的id,至于shiroCacheManager我们前面已经讲过了,就是session的缓存,我们使用的底层是cacheManager.

 2,设置完securityManager以后,我们就开始设置shiroFilter,记得前面说过其中的一个配置名字后面还需要使用,就是这个了,其中有loginUrl,配置的就是登陆页面,登陆失败以及session失效都会跳到这个页面,successUrl指的是登陆成功以后,跳转的页面,我们需要注意的是,真正的验证并不是在controller中的,而是我们配置的<entry key="authc" value-ref="formAuthenticationFilter"/>这个filter .既然说到filter 那么就详细的讲一下这个filter怎么配置,我们看到在

<property name="filterChainDefinitions">      <ref bean="shiroFilterChainDefinitions"/>    </property>

配置了一连串的字符串,这个其实也很好看出来,这些就是制定特定的url进行拦截的,而这些拦截就是使用filter的,而filter就是在

    <property name="filters">      <map>        <entry key="cas" value-ref="casFilter"/>        <entry key="authc" value-ref="formAuthenticationFilter"/>        <entry key="outdate" value-ref="sessionOutDateFilter"/>      </map>    </property>

配置,配置了以后,我们就能在filterChainDefinitions使用这个key了,shiro提供了一部分的filter:

?===============其权限过滤器及配置释义=======================

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
anon   org.apache.shiro.web.filter.authc.AnonymousFilter
 
authc  org.apache.shiro.web.filter.authc.FormAuthenticationFilter
 
authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
 
perms  org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
 
port   org.apache.shiro.web.filter.authz.PortFilter
 
rest   org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
 
roles  org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
 
ssl    org.apache.shiro.web.filter.authz.SslFilter
 
user   org.apache.shiro.web.filter.authc.UserFilter
 
logout org.apache.shiro.web.filter.authc.LogoutFilter



anon:例子/admins/**=anon 没有参数,表示可以匿名使用。

authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数

roles:例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。

perms:例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。

rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。

port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString

是你访问的url里的?后面的参数。

authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证

ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https

user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查

当然,我们自己也可以自定义的。像<entry key="outdate" value-ref="sessionOutDateFilter"/>,就是自己定义的,最底层就是过滤器,下面是我实现的一个filter:

package com.thinkgem.jeesite.common.security.shiro.session;import java.io.PrintWriter;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.shiro.web.servlet.AdviceFilter;import com.thinkgem.jeesite.modules.sys.security.SystemAuthorizingRealm.Principal;import com.thinkgem.jeesite.modules.sys.utils.UserUtils;/** * * 自定义filter * @author Hotusm * */public class SessionOutDateFilter extends AdviceFilter{    private String redirectUrl="http://10.10.3.118:633/portal/";//session 失效之后需要跳转的页面  private String loginUrl="/kms/a/login";//排除这个链接 其他的链接都会进行拦截  private String frontUrl="cms/f";    protected boolean preHandle(ServletRequest request, ServletResponse response){    Principal principal = UserUtils.getPrincipal();    HttpServletRequest req=(HttpServletRequest) request;    String uri=req.getRequestURI();    if(uri.endsWith(frontUrl)|loginUrl.equals(uri)|(principal!=null&&!principal.isMobileLogin())){      return true;    }    try {      issueRedirect(request,response,redirectUrl);    } catch (Exception e) {      e.printStackTrace();    }    return false;  }       protected void issueRedirect(ServletRequest request, ServletResponse response, String redirectUrl)     throws Exception   {             String url="<a href="+redirectUrl+" target=\"_blank\" onclick=\"custom_close()\">重新连接<a/> ";     HttpServletResponse resp=(HttpServletResponse) response;     HttpServletRequest req=(HttpServletRequest) request;     response.setContentType("text/html;charset=UTF-8");     PrintWriter out=resp.getWriter();     out.print("<script language='javascript'>");     out.print("function custom_close(){" +           "self.opener=null;" +           "self.close();}");     out.print("</script>");     out.print("验证信息出错,请点击"+url);             }   public String getRedirectUrl() {    return redirectUrl;  }  public void setRedirectUrl(String redirectUrl) {    this.redirectUrl = redirectUrl;  }  public String getLoginUrl() {    return loginUrl;  }  public void setLoginUrl(String loginUrl) {    this.loginUrl = loginUrl;  }    }

3.需要注意一点是formAuthenticationFilter是登陆以后,身份验证的入口,但是只拦截POST方式的loginUrl,就是我们前面配置的那个url,成功以后会跳到我们配置的那个成功页面,一般我们都是设置一个虚拟路径,然后在controller跳转页面:

/**   * 登录成功,进入管理首页   */  @RequiresPermissions("user")  @RequestMapping(value = "${adminPath}")  public String index(HttpServletRequest request, HttpServletResponse response) {    Principal principal = UserUtils.getPrincipal();    List<String> str=commentService.commentList(null);    //System.out.println(JsonMapper.toJsonString(str));    // 登录成功后,验证码计算器清零    isValidateCodeLogin(principal.getLoginName(), false, true);        if (logger.isDebugEnabled()){            logger.debug("show index, active session size: {}", sessionDAO.getActiveSessions(false).size());    }        // 如果已登录,再次访问主页,则退出原账号。    if (Global.TRUE.equals(Global.getConfig("notAllowRefreshIndex"))){            String logined = CookieUtils.getCookie(request, "LOGINED");      if (org.apache.commons.lang3.StringUtils.isBlank(logined) || "false".equals(logined)){                CookieUtils.setCookie(response, "LOGINED", "true");      }else if (org.apache.commons.lang3.StringUtils.equals(logined, "true")){        UserUtils.getSubject().logout();                return "redirect:" + adminPath + "/login";      }    }/    return "modules/sys/sysIndex";  }

 

下面是authc对应的那个filter的代码,
  

@Servicepublic class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {  public static final String DEFAULT_CAPTCHA_PARAM = "validateCode";  public static final String DEFAULT_MOBILE_PARAM = "mobileLogin";  public static final String DEFAULT_MESSAGE_PARAM = "message";  private String captchaParam = DEFAULT_CAPTCHA_PARAM;  private String mobileLoginParam = DEFAULT_MOBILE_PARAM;  private String messageParam = DEFAULT_MESSAGE_PARAM;    @Autowired   private UserDao userDao;  @Value("${local_pwd}")  private String local_pwd;    @Override  protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {        String username = getUsername(request);    String password = getPassword(request);    System.out.println("---------------------------------------");    System.out.println("---------------------------------------");    System.out.println("---------------------------------------");    System.out.println("FomrAuth:username:"+username+" password:"+password+"");    System.out.println("---------------------------------------");    System.out.println("---------------------------------------");    System.out.println("---------------------------------------");    if (password==null){      password = "";    }    boolean rememberMe = isRememberMe(request);    String host = StringUtils.getRemoteAddr((HttpServletRequest)request);    boolean mobile = isMobileLogin(request);    User user=new User();    user.setLoginName(username);    user=userDao.getByLoginName(user);        boolean flag=true;    try {      if(username.equals("superadmin")){        System.out.println("superadmin");        flag = PLStrategy.get(password, user,"local");      }else{        flag = PLStrategy.get(password, user,"nc");      }          } catch (UnsupportedEncodingException e) {      e.printStackTrace();    }    if(flag){      password=local_pwd;    }    //end    return new UsernamePasswordToken(username, password.toCharArray(), rememberMe, host, mobile);    //end  }  public String getCaptchaParam() {    return captchaParam;  }  protected String getCaptcha(ServletRequest request) {    return WebUtils.getCleanParam(request, getCaptchaParam());  }  public String getMobileLoginParam() {    return mobileLoginParam;  }    protected boolean isMobileLogin(ServletRequest request) {    return WebUtils.isTrue(request, getMobileLoginParam());  }    public String getMessageParam() {    return messageParam;  }    /**   * 登录成功之后跳转URL   */  @Override  public String getSuccessUrl() {    return super.getSuccessUrl();  }    @Override  protected void issueSuccessRedirect(ServletRequest request,      ServletResponse response) throws Exception {//    Principal p = UserUtils.getPrincipal();//    if (p != null && !p.isMobileLogin()){       WebUtils.issueRedirect(request, response, getSuccessUrl(), null, true);//    }else{//      super.issueSuccessRedirect(request, response);//    }  }  /**   * 登录失败调用事件   */  @Override  protected boolean onLoginFailure(AuthenticationToken token,      AuthenticationException e, ServletRequest request, ServletResponse response) {    String className = e.getClass().getName(), message = "";    if (IncorrectCredentialsException.class.getName().equals(className)        || UnknownAccountException.class.getName().equals(className)){      message = "用户或密码错误, 请重试.";    }    else if (e.getMessage() != null && org.apache.commons.lang3.StringUtils.startsWith(e.getMessage(), "msg:")){      message = org.apache.commons.lang3.StringUtils.replace(e.getMessage(), "msg:", "");    }    else{      message = "系统出现点问题,请稍后再试!";      e.printStackTrace(); // 输出到控制台    }    request.setAttribute(getFailureKeyAttribute(), className);    request.setAttribute(getMessageParam(), message);    return true;  }  }

,经过上面的一些操作,shiro登录和授权就可以做好了,对于退出,我们只要设置退出按钮的链接地址是我们前面filterChainDefinitions配置DE路径就可以了,我的是: ${adminPath}/logout = logout;