除了基本的以外,我們還需要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中設置配置文件的時候,直接使用通配符了:
<!-- 配置spring容器的路徑 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/spring-context-*.xml</param-value>
</context-param>
<!-- 對spring開始監聽 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
這樣就可以掃描到兩個配置文件了,又不會掃描到我們的spring-mvc.xml了,
2除了在web.xml中設置這個以外,我們還需要設置spring-mvc的位置:
<!-- 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.xml</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.xml中配置shiroFilter:
<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.xml:先把配置的貼出來,然后講一下這幾個配置的意義:
<?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:context="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" class="com.yonyou.hotusm.common.security.shiro.session.JedisSessionDAO"> -->
<!-- <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" class="com.thinkgem.jeesite.common.security.shiro.cache.SessionCacheManager" /> -->
<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; @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();
// 如果是靜態文件,則不更新SESSION
if (Servlets.isStaticFile(uri)){
return;
}
// 如果是視圖文件,則不更新SESSION
if (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();
// 如果是靜態文件,則不創建SESSION
if (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
*/
@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;
}
}
// 過濾掉的SESSION
if (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;
}
//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;
