spring+shiro 整合之自己注册会话和自写realm

Apache Shiro是java的一个安全框架。目前,使用Apache Shiro的人越来越多,因为它相当简单,对比SPRing Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。
因为我总结的是使用SpringMVC和Apache Shiro整合,注重的是整合和使用,至于基础,我这里就不细说了.按照惯例,既然是需要创建项目,那么我们首先需要JAR包,Apache shiro的架包除了除了基本的以外,我们还需要shiro-web和shiro-spring的的架包,下面是所需要的所有shiro架包,至于其他的架包,像缓存的架包,Spring和SpringMVC的架包还是和我们以前使用的架包一样的。

org.apache.shiro shiro-core 1.2.3 org.apache.shiro shiro-ehcache 1.2.3 org.apache.shiro shiro-web 1.2.3 org.apache.shiro shiro-spring 1.2.3

所有的架包都搞清楚了以后,我们就可以开始正式搭建了,在myeclise中创建一个maven项目,将需要的架包信息依赖全部放入。下面就分步骤来创建
1.首先创建spring的配置文件,位置都在在resource中,配置文件是spring-context.xml,创建Apache Shiro的配置文件,名字是spring-context-shiro.xml,还有一个配置文件是springmvc的,配置文件是spring-mvc,这样起名是有原因的,因为这样我们就可以在web.xml中设置配置文件的时候,直接使用通配符了:

contextConfigLocation classpath*:/spring-context-*.xml org.springframework.web.context.ContextLoaderListener

这样就可以扫描到两个配置文件了,又不会扫描到我们的spring-mvc.xml了,
【spring+shiro 整合之自己注册会话和自写realm】 2除了在web.xml中设置这个以外,我们还需要设置spring-mvc的位置:
springServlet org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:springmvc.xml 1 springServlet /

3.在web.xml中配置shiroFilter:
shiroFilter org.springframework.web.filter.DelegatingFilterProxy shiroFilter /*

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

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

Shiro Configuration /static/** = anon /userfiles/** = anon ${adminPath}/cas = cas ${adminPath}/login = authc ${adminPath}/logout = logout ${adminPath}/** = user


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来源与urlrequest.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 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不抛出异常}}@Overrideprotected 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; }@Overridepublic 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(); } @Overrideprotected 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() : ""); } @Overrideprotected void doDelete(Session session) {if (session == null || session.getId() == null) {return; }super.doDelete(session); logger.debug("delete {} ", session.getId()); } @Overrideprotected 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(); } @Overrideprotected Session doReadSession(Serializable sessionId) {return super.doReadSession(sessionId); }@Overridepublic 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 */@Overridepublic Collection getActiveSessions(boolean includeLeave) {return getActiveSessions(includeLeave, null, null); }/** * 获取活动会话 * @param includeLeave 是否包括离线(最后访问时间大于3分钟为离线会话) * @param principal 根据登录者对象获取活动会话 * @param filterSession 不为空,则过滤掉(不包含)这个会话。 * @return */@Overridepublic Collection getActiveSessions(boolean includeLeave, Object principal, Session filterSession) {// 如果包括离线,并无登录者条件。if (includeLeave && principal == null){return getActiveSessions(); }Set 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中的,而是我们配置的这个filter .既然说到filter 那么就详细的讲一下这个filter怎么配置,我们看到在

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

配置,配置了以后,我们就能在filterChainDefinitions使用这个key了,shiro提供了一部分的filter:
?===============其权限过滤器及配置释义=======================
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 anonorg.apache.shiro.web.filter.authc.AnonymousFilter authcorg.apache.shiro.web.filter.authc.FormAuthenticationFilter authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter permsorg.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter portorg.apache.shiro.web.filter.authz.PortFilter restorg.apache.shiro.web.filter.authz.HttpMethodPermissionFilter rolesorg.apache.shiro.web.filter.authz.RolesAuthorizationFilter sslorg.apache.shiro.web.filter.authz.SslFilter userorg.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没有参数表示必须存在用户,当登入操作时不做检查
当然,我们自己也可以自定义的。像,就是自己定义的,最底层就是过滤器,下面是我实现的一个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="重新连接 "; HttpServletResponse resp=(HttpServletResponse) response; HttpServletRequest req=(HttpServletRequest) request; response.setContentType("text/html; charset=UTF-8"); PrintWriter out=resp.getWriter(); out.print(""); 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 = "https://www.it610.com/article/${adminPath}") public String index(HttpServletRequest request, HttpServletResponse response) { Principal principal = UserUtils.getPrincipal(); List 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;

    推荐阅读