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.xml,创建Apache Shiro的配置文件,名字是spring-context-shiro.xml,还有一个配置文件是springmvc的,配置文件是spring-mvc,这样起名是有原因的,因为这样我们就可以在web.xml中设置配置文件的时候,直接使用通配符了:
  

  1. <!-- 配置spring容器的路径 -->
  2. <context-param>
  3. <param-name>contextConfigLocation</param-name>
  4. <param-value>classpath*:/spring-context-*.xml</param-value>
  5. </context-param>
  6. <!-- 对spring开始监听 -->
  7. <listener>
  8. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  9. </listener>

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

2除了在web.xml中设置这个以外,我们还需要设置spring-mvc的位置:

  1. <!-- MVC Servlet
  2. 设置springmvc的Servlet
  3. -->
  4. <servlet>
  5. <servlet-name>springServlet</servlet-name>
  6. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  7. <init-param>
  8. <param-name>contextConfigLocation</param-name>
  9. <param-value>classpath:springmvc.xml</param-value>
  10. </init-param>
  11. <load-on-startup>1</load-on-startup>
  12. </servlet>
  13. <servlet-mapping>
  14. <servlet-name>springServlet</servlet-name>
  15. <url-pattern>/</url-pattern>
  16. </servlet-mapping>

3.在web.xml中配置shiroFilter:

  1. <filter>
  2. <filter-name>shiroFilter</filter-name>
  3. <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  4. </filter>
  5. <filter-mapping>
  6. <filter-name>shiroFilter</filter-name>
  7. <url-pattern>/*</url-pattern>
  8. </filter-mapping>

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

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

spring的其他的配置,该怎样还是这样,我们的重点是配置spring-context-shiro.xml:先把配置的贴出来,然后讲一下这几个配置的意义:

  

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
  4. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
  5. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"
  6. default-lazy-init="true">
  7. <description>Shiro Configuration</description>
  8. <!-- 加载配置属性文件 -->
  9. <context:property-placeholder ignore-unresolvable="true" location="classpath:yonyou.properties" />
  10. <!-- Shiro权限过滤过滤器定义 -->
  11. <bean name="shiroFilterChainDefinitions" class="java.lang.String">
  12. <constructor-arg>
  13. <value>
  14. /static/** = anon
  15. /userfiles/** = anon
  16. ${adminPath}/cas = cas
  17. ${adminPath}/login = authc
  18. ${adminPath}/logout = logout
  19. ${adminPath}/** = user
  20. </value>
  21. </constructor-arg>
  22. </bean>
  23. <!-- 安全认证过滤器 -->
  24. <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
  25. <property name="securityManager" ref="securityManager" /><!--
  26. <property name="loginUrl" value="${cas.server.url}?service=${cas.project.url}${adminPath}/cas" /> -->
  27. <property name="loginUrl" value="${adminPath}/login" />
  28. <property name="successUrl" value="${adminPath}?login" />
  29. <property name="filters">
  30. <map>
  31. <entry key="cas" value-ref="casFilter"/>
  32. <entry key="authc" value-ref="formAuthenticationFilter"/>
  33. </map>
  34. </property>
  35. <property name="filterChainDefinitions">
  36. <ref bean="shiroFilterChainDefinitions"/>
  37. </property>
  38. </bean>
  39. <!-- CAS认证过滤器 -->
  40. <bean id="casFilter" class="org.apache.shiro.cas.CasFilter">
  41. <property name="failureUrl" value="${adminPath}/login"/>
  42. </bean>
  43. <!-- 定义Shiro安全管理配置 -->
  44. <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
  45. <property name="realm" ref="systemAuthorizingRealm" />
  46. <property name="sessionManager" ref="sessionManager" />
  47. <property name="cacheManager" ref="shiroCacheManager" />
  48. </bean>
  49. <!-- 自定义会话管理配置 -->
  50. <bean id="sessionManager" class="com.yonyou.hotusm.common.security.session.SessionManager">
  51. <property name="sessionDAO" ref="sessionDAO"/>
  52. <!-- 会话超时时间,单位:毫秒 -->
  53. <property name="globalSessionTimeout" value="${session.sessionTimeout}"/>
  54. <!-- 定时清理失效会话, 清理用户直接关闭浏览器造成的孤立会话 -->
  55. <property name="sessionValidationInterval" value="${session.sessionTimeoutClean}"/>
  56. <!-- <property name="sessionValidationSchedulerEnabled" value="false"/> -->
  57. <property name="sessionValidationSchedulerEnabled" value="true"/>
  58. <property name="sessionIdCookie" ref="sessionIdCookie"/>
  59. <property name="sessionIdCookieEnabled" value="true"/>
  60. </bean>
  61. <!-- 指定本系统SESSIONID, 默认为: JSESSIONID 问题: 与SERVLET容器名冲突, 如JETTY, TOMCAT 等默认JSESSIONID,
  62. 当跳出SHIRO SERVLET时如ERROR-PAGE容器会为JSESSIONID重新分配值导致登录会话丢失! -->
  63. <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
  64. <constructor-arg name="name" value="jeesite.session.id"/>
  65. </bean>
  66. <!-- 自定义Session存储容器 -->
  67. <!-- <bean id="sessionDAO" class="com.yonyou.hotusm.common.security.shiro.session.JedisSessionDAO"> -->
  68. <!-- <property name="sessionIdGenerator" ref="idGen" /> -->
  69. <!-- <property name="sessionKeyPrefix" value="${redis.keyPrefix}_session_" /> -->
  70. <!-- </bean> -->
  71. <bean id="sessionDAO" class="com.yonyou.hotusm.common.security.session.CacheSessionDAO">
  72. <property name="sessionIdGenerator" ref="idGen" />
  73. <property name="activeSessionsCacheName" value="activeSessionsCache" />
  74. <property name="cacheManager" ref="shiroCacheManager" />
  75. </bean>
  76. <!-- 定义授权缓存管理器 -->
  77. <!-- <bean id="shiroCacheManager" class="com.thinkgem.jeesite.common.security.shiro.cache.SessionCacheManager" /> -->
  78. <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
  79. <property name="cacheManager" ref="cacheManager"/>
  80. </bean>
  81. <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
  82. <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
  83. <!-- AOP式方法级权限检查 -->
  84. <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
  85. <property name="proxyTargetClass" value="true" />
  86. </bean>
  87. <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
  88. <property name="securityManager" ref="securityManager"/>
  89. </bean>
  90. </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;

@Service

public class SystemAuthorizingRealm extends AuthorizingRealm{

@Autowired

private UserDao userDao;

@Override

protected AuthorizationInfo doGetAuthorizationInfo(

PrincipalCollection principals) {

SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();

info.addStringPermission("sys:manager");

info.addStringPermission("user");

System.out.println("开始授权");

return info;

}

@Override

protected 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 "";

}

}

@Override

public 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();

}

@Override

protected Serializable getSessionId(ServletRequest request, ServletResponse response) {

// 如果参数中包含“__sid”参数,则使用此sid会话。 例如:http://localhost/project?__sid=xxx&__cookie=true
String 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); } } @Override public void validateSessions() { super.validateSessions(); } @Override protected Session retrieveSession(SessionKey sessionKey) { try{ return super.retrieveSession(sessionKey); }catch (UnknownSessionException e) { // 获取不到SESSION不抛出异常return null; } } @Override public Date getStartTimestamp(SessionKey key) { try{ return super.getStartTimestamp(key); }catch (InvalidSessionException e) { // 获取不到SESSION不抛出异常return null; } } @Override public Date getLastAccessTime(SessionKey key) { try{ return super.getLastAccessTime(key); }catch (InvalidSessionException e) { // 获取不到SESSION不抛出异常return null; } } @Override public long getTimeout(SessionKey key){ try{ return super.getTimeout(key); }catch (InvalidSessionException e) { // 获取不到SESSION不抛出异常return 0; } } @Override public void setTimeout(SessionKey key, long maxIdleTimeInMillis) { try{ super.setTimeout(key, maxIdleTimeInMillis); }catch (InvalidSessionException e) { // 获取不到SESSION不抛出异常
} } @Override public void touch(SessionKey key) { try{ super.touch(key); }catch (InvalidSessionException e) { // 获取不到SESSION不抛出异常
} } @Override public String getHost(SessionKey key) { try{ return super.getHost(key); }catch (InvalidSessionException e) { // 获取不到SESSION不抛出异常return null; } } @Override public Collection<Object> getAttributeKeys(SessionKey key) { try{ return super.getAttributeKeys(key); }catch (InvalidSessionException e) { // 获取不到SESSION不抛出异常return null; } } @Override public Object getAttribute(SessionKey sessionKey, Object attributeKey) { try{ return super.getAttribute(sessionKey, attributeKey); }catch (InvalidSessionException e) { // 获取不到SESSION不抛出异常return null; } } @Override public void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) { try{ super.setAttribute(sessionKey, attributeKey, value); }catch (InvalidSessionException e) { // 获取不到SESSION不抛出异常
} } @Override public Object removeAttribute(SessionKey sessionKey, Object attributeKey) { try{ return super.removeAttribute(sessionKey, attributeKey); }catch (InvalidSessionException e) { // 获取不到SESSION不抛出异常return null; } } @Override public void stop(SessionKey key) { try{ super.stop(key); }catch (InvalidSessionException e) { // 获取不到SESSION不抛出异常
} } @Override public 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; } } @Override protected 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;

}

// 手动控制不更新SESSION
String 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(); // 如果是静态文件,则不获取SESSIONif (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*/ @Override public Collection<Session> getActiveSessions(boolean includeLeave) { return getActiveSessions(includeLeave, null, null); } /** * 获取活动会话 * @param includeLeave 是否包括离线(最后访问时间大于3分钟为离线会话) * @param principal 根据登录者对象获取活动会话 * @param filterSession 不为空,则过滤掉(不包含)这个会话。 * @return*/ @Override public 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的代码,
  

@Service
public 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;
}
//endreturn 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;

来源:http://www.cnblogs.com/zr520/p/5009790.html

Shiro 整合SpringMVC 并且实现权限管理的更多相关文章

  1. Shiro 整合SpringMVC 并实现权限管理,登录和注销

    Shiro 整合SpringMVC 并且实现权限管理,登录和注销 Apache Shiro是Java的一个安全框架.目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring S ...

  2. Shiro 整合SpringMVC 并且实现权限管理,登录和注销

    Apache Shiro是Java的一个安全框架.目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大 ...

  3. (十二)整合 Shiro 框架,实现用户权限管理

    整合 Shiro 框架,实现用户权限管理 1.Shiro简介 1.1 基础概念 1.2 核心角色 1.3 核心理念 2.SpringBoot整合Shiro 2.1 核心依赖 2.2 Shiro核心配置 ...

  4. SpringBoot2.0 整合 Shiro 框架,实现用户权限管理

    本文源码:GitHub·点这里 || GitEE·点这里 一.Shiro简介 1.基础概念 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理.作为一款安全 ...

  5. Spring Cloud之路:(七)SpringBoot+Shiro实现登录认证和权限管理

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/sage_wang/article/details/79592269一.Shiro介绍1.Shiro是 ...

  6. shiro整合springmvc

    说明   代码及部分相关资料根据慕课网Mark老师的视频进行整理   其他资料: shiro官网 流程 配置 1) 配置web.xml整合shiro 把shiro整合到springMVC实质上是在we ...

  7. Apache Shiro(五)-登录认证和权限管理ssm

    创建一个web动态项目 jar包 web.xml web.xml做了如下几件事情1. 指定spring的配置文件有两个 applicationContext.xml: 用于链接数据库的 applica ...

  8. Shiro集成SSM基于URL权限管理(一)

    学习了shiro之后,我们就可以说尝试把shiro加入ssm中,并做一套基于URL的权限管理. 其他的准备工作就不多说了,直接动手操作,看到效果再去理解. 表结构 执行如下,数据库名字可以自行修改,不 ...

  9. Spring boot 入门(四):集成 Shiro 实现登陆认证和权限管理

    本文是接着上篇博客写的:Spring boot 入门(三):SpringBoot 集成结合 AdminLTE(Freemarker),利用 generate 自动生成代码,利用 DataTable 和 ...

随机推荐

  1. VB6.0对鼠标滚轮不支持的解决方法[转]已经测试work

    今天要修改一个老DLL文件,安装了vb6,用起来很不爽. VB6编辑器 和 VBA编辑器 (Office 中的VB编辑器)都不支持鼠标滚动. 但 MS 已经提供了补丁http://download.m ...

  2. Druid.io索引过程分析——时间窗,列存储,LSM树,充分利用内存,concise压缩

    Druid底层不保存原始数据,而是借鉴了Apache Lucene.Apache Solr以及ElasticSearch等检索引擎的基本做法,对数据按列建立索引,最终转化为Segment,用于存储.查 ...

  3. AIX修改用户密码登录不成功案例分享

    背景:使用passwd XXXX fcesjaif,修改新密码仍然提示密码不正确.拒绝登录 a. 使用命令lsuser -f XXXX |grep -i successful 查看不成功的次数 chu ...

  4. terminator终端工具

    terminator是个很好的终端程序,在Ubuntu Linux下安装如下: sudo apt-get install terminator 可在同一屏打开多个窗口:

  5. 用python定时文章发布wordpress

    用python定时文章发布wordpress: 流程: 采集 - 筛选文章 - wordpress文章发布. wordpress文章发布代码:python利用模块xmlrpclib发布文章非常便捷,省 ...

  6. valueOf() toString() typeof instanceof

    ******在chrome console中运行{a:1}.valueOf(); 报错:"SyntaxError: Unexpected token . ",这是由于{}被js引擎 ...

  7. String与InputStream相互转换

    1.String to InputStream String str = "String与InputStream相互转换"; InputStream   in_nocode   = ...

  8. Ionic 2.0.0-rc.1 发布,HTML5 移动应用框架

    Ionic 2.0.0-rc.1 发布了,Ionic Framework 是个高级的 HTML5 移动端应用框架,是个很漂亮的使用 HTML5 开发混合移动应用前端框架.本次更新内容如下: Bug 修 ...

  9. iOS 模拟器上录制Demo视频

    在App审核中会让用户上传一段简易的视频,那么如何制作改视频呢? 今天无意中在Cocochina 中看到 说利用QuickTime来制作,而QuickTime是操作系统自带的. 哦 My,God ! ...

  10. JS rem 设置

    (function () { var docEl = document.documentElement; var resize = 'orientationchange' in window ? 'o ...