springmvc 功能 登出_Shiro 整合SpringMVC 并且实现权限管理,登录和注销

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

因为我总结的是使用SpringMVC和Apache Shiro整合,注重的是整合和使用,至于基础,我这里就不细说了。我使用的是maven进行项目的构建,对于非maven的项目只要把这些JAR包下载下来放到相应的位置即可。因为这个项目是整合Spring的,所以除了Apache shiro的JAR之外,我们还需要shiro-web和shiro-spring的的JAR,下面是所需要的所有shiro架包,至于其他的架包,像缓存的架包,Spring和SpringMVC的架包等等还是平时那些通用JAR,没有多余的。

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

将JAR都准备好了之后,我们就可以开始正式搭建了。下面就分步骤来创建

1.

一:首先创建spring的配置文件,位置都在resource中(非maven的项目可以放到classpath或者是WEB-INF下面,只要保证最后编译之后能在classpath下即可),配置文件为spring-context.xml.

二:创建Apache Shiro的配置文件,名字是spring-context-shiro.xml,我们只需要和spring的配置文件放在同一级就可以了。

三:还有一个配置文件是springmvc的,配置文件是spring-mvc。前面两个文件都是以spring-context*开头是有原因的,因为这样我们就可以在web.xml中设置配置文件的时候,直接使用通配符扫描前两个但是又可以不扫描springmvc的配置文件

这是在web.xml里面配置:

contextConfigLocation

classpath*:/spring-context-*.xml

org.springframework.web.context.ContextLoaderListener

除了spring的配置,还有一个配置是非常重要的:shiroFilter。对于初次配置shiro的同学经常遇到一个问题:问题大概讲的是shiroFilter找不到,但是我们明明在web.xml和spring-context-shiro配置文件里面配置了呀,怎么回事?这是因为这个shiroFilter名字两边需要一致!!!(是不是很坑,但是其实是可以配置的,只是一般人不知道,这个后面讲)

shiroFilter

org.springframework.web.filter.DelegatingFilterProxy

shiroFilter

/*

3.

除了在web.xml中设置spring和spring-shiro配置文件位置之外,我们还需要在web.xml中设置spring-mvc的位置:

springServlet

org.springframework.web.servlet.DispatcherServlet

contextConfigLocation

classpath:springmvc.xml

1

springServlet

/

4

在spring-context配置文件中,还有一个是需要配置-cacheManager,因为shiro的session是自己实现的,所以我们还需要一个缓存框架,所以在spring的配置文件一定要注意配置哦,用的是ehcache

Ehcache的maven地址:

net.sf.ehcache

ehcache-core

2.6.9

org.apache.shiro

shiro-ehcache

1.2.3

5

在项目中重点还是配置spring-context-shiro.xml:先把配置的贴出来,然后讲一下这几个配置的意义:

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

Shiro Configuration

/static/** = anon

/userfiles/** = anon

${adminPath}/login = authc

${adminPath}/logout = logout

${adminPath}/** = user

这里从上往下进行解释:

1.shiroFilterChainDefinitions

可以看到类型是String,String内部的各个字符串是使用"\n\t"进行换行。这里的每一行代表了一个路由,而后面的anno,user等等,也就是相对应的Filter(这块我们是可以自己定义的,后面会讲,${adminPath} 是我在配置文件里面配置的路径而已,完全可以根据自己的路由进行设置。shiroFilterChainDefinitions最主要是在shiroFilter中作为一个参数注入。

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

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没有参数表示必须存在用户,当登入操作时不做检查

2.重点来了:shiroFilter(ShiroFilterFactoryBean),这里要非常小心!! 这里的bean的名字一定要和web.xml里面的那个Filter名字相同,具体可以见下面的源码:

DelegatingFilterProxy.java:

@Overrideprotected void initFilterBean() throwsServletException {synchronized (this.delegateMonitor) {if (this.delegate == null) {//If no target bean name specified, use filter name.

if (this.targetBeanName == null) {this.targetBeanName =getFilterName();

}//Fetch Spring root application context and initialize the delegate early,//if possible. If the root application context will be started after this//filter proxy, we'll have to resort to lazy initialization.

WebApplicationContext wac =findWebApplicationContext();if (wac != null) {this.delegate =initDelegate(wac);

}

}

}

}

还记得我们web.xml里面配置的那个Filter吗, 其实我们配置的Filter只不过是起到一个代理的作用,那么它代理谁呢? 它也不能知道,它所能做的就是根据targetBeanName去容器中获取bean(这个bean是实现了Filter接口的),其中的targetBeanName就是bean的名称,如果没有设置的话,那么就默认使用的Filter名称。所以说前面说过的必须相同是不正确的,你只需要在Filter中设置targetBeanName和spring-context-shiro配置文件中ShiroFilterFactoryBean的bean名称一样即可。

除了上面需要注意的几个点之外,ShiroFilterFactoryBean还有一些属性:unauthorizedUrl,系统未认证时跳转的页面,loginUrl登录页面,successUrl登录成功的页面,filter属性就是和前面的shiroFilterChainDefinitions对应的。同时支持自定义,并且配置路由:像这样的。最底层是过滤器,下面是我实现的一个filter:

packagecom.yonyou.kms.common.security.shiro.session;importjava.io.PrintWriter;importjavax.servlet.ServletRequest;importjavax.servlet.ServletResponse;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importorg.apache.shiro.web.servlet.AdviceFilter;importcom.yonyou.kms.modules.sys.security.SystemAuthorizingRealm.Principal;importcom.yonyou.kms.modules.sys.utils.UserUtils;/***

* 自定义filter

*@authorHotusm

**/

public class SessionOutDateFilter extendsAdviceFilter{private String redirectUrl="http://url/portal";//session 失效之后需要跳转的页面

private String platformUrl="http://url/kms/a/login";//排除这个链接 其他的链接都会进行拦截

private String loginUrl="/kms/a/login";private String frontUrl="cms/f";private String uploadUrl="cms/article/plupload";private String appUrl="a/app";protected booleanpreHandle(ServletRequest request, ServletResponse response){

Principal principal=UserUtils.getPrincipal();

HttpServletRequest req=(HttpServletRequest) request;

String uri=req.getRequestURI();if(checkUrl(uri, loginUrl,frontUrl,uploadUrl,appUrl)|(principal!=null&&!principal.isMobileLogin())){return true;

}try{

issueRedirect(request,response,redirectUrl);

}catch(Exception e) {

e.printStackTrace();

}return false;

}protected voidissueRedirect(ServletRequest request, ServletResponse response, String redirectUrl)throwsException

{

String url="重新登录 ";

String platform="直接登录 ";

HttpServletResponse resp=(HttpServletResponse) response;

HttpServletRequest req=(HttpServletRequest) request;

response.setContentType("text/html;charset=UTF-8");

PrintWriter out=resp.getWriter();

out.print("

out.print("function custom_close(){" +

"self.opener=null;" +

"self.close();}");

out.print("");

out.print("没有权限或者验证信息过期,请点击"+url+"登录portal
");

out.print("直接登录"+platform);

}publicString getRedirectUrl() {returnredirectUrl;

}public voidsetRedirectUrl(String redirectUrl) {this.redirectUrl =redirectUrl;

}publicString getLoginUrl() {returnloginUrl;

}public voidsetLoginUrl(String loginUrl) {this.loginUrl =loginUrl;

}/*** 排除一些url不进行拦截

*@paramtargetUrl

*@paramurls

*@return

*/

private booleancheckUrl(String targetUrl,String ...urls){for(int i=0;i

}

}return false;

}

}

这个和springmvc的拦截器是相同的用法,返回true则表示验证通过(后面的逻辑继续执行),返回false就表示验证不通过。

最后在shiroFilter的filters进行配置我们自定义的bean:

这个sessionOutDateFilter我们需要注入(这里省略)。最后我们就将可以将这些东西加到shiroFilterChainDefinitions中去:

......

${adminPath}/** = outdate

.....

这样我们自己定义的叫做outdata的路由会拦截${adminPath}下的所以路径,并且进行验证。

3.

SecurityManager

它和我们前面讲的ShiroFilterFactoryBean的关系形象的将就是ShiroFilterFactoryBean是一个路由规则配置仓库和代理类,其实真正的逻辑都是在SecurityManager中进行的,下面来进行详讲SecurityManager的依赖类。

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

packagecom.yonyou.hotusm.module.sys.security;importcom.yonyou.hotusm.common.utils.Encodes;importcom.yonyou.hotusm.module.sys.dao.UserDao;importcom.yonyou.hotusm.module.sys.entity.User;importcom.yonyou.hotusm.module.sys.service.UserService;importcom.yonyou.hotusm.module.sys.util.UserUtils;import org.apache.shiro.authc.*;importorg.apache.shiro.authc.credential.HashedCredentialsMatcher;importorg.apache.shiro.authz.AuthorizationInfo;importorg.apache.shiro.authz.SimpleAuthorizationInfo;importorg.apache.shiro.authz.UnauthenticatedException;importorg.apache.shiro.realm.AuthorizingRealm;importorg.apache.shiro.subject.PrincipalCollection;importorg.apache.shiro.util.ByteSource;importorg.springframework.beans.factory.InitializingBean;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;importjava.io.Serializable;

@Service("systemAuthorizingRealm")public class SystemAuthorizingRealm extends AuthorizingRealm implementsInitializingBean{

@AutowiredprivateUserDao userDao;

@OverrideprotectedAuthorizationInfo doGetAuthorizationInfo(

PrincipalCollection principals) {

SimpleAuthorizationInfo info=newSimpleAuthorizationInfo();

info.addStringPermission("sys:manager");

info.addStringPermission("user");

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

}

@OverrideprotectedAuthenticationInfo doGetAuthenticationInfo(

AuthenticationToken token)throwsAuthenticationException {

UsernamePasswordToken upToken=(UsernamePasswordToken) token;

String username=upToken.getUsername();

User user=newUser();

user.setLoginName(username);

user=userDao.get(user);if(user!=null){byte[] salt = Encodes.decodeHex(user.getPassword().substring(0,16));return newSimpleAuthenticationInfo(username,

user.getPassword().substring(16), ByteSource.Util.bytes(salt), getName());

}else{throw newUnauthenticatedException();

}

}public static class Principal implementsSerializable {private static final long serialVersionUID = 1L;private String id; //编号

private String loginName; //登录名

private String name; //姓名

publicPrincipal(User user) {this.id =user.getId();this.loginName =user.getLoginName();this.name =user.getName();

}publicString getId() {returnid;

}publicString getLoginName() {returnloginName;

}publicString getName() {returnname;

}/*** 获取SESSIONID*/

publicString getSessionid() {try{return(String) UserUtils.getSession().getId();

}catch(Exception e) {return "";

}

}

@OverridepublicString toString() {returnid;

}

}//在bean初始化完成以后 设置校验的规则

public void afterPropertiesSet() throwsException {

HashedCredentialsMatcher matcher= newHashedCredentialsMatcher(UserService.HASH_ALGORITHM);

matcher.setHashIterations(UserService.HASH_INTERATIONS);

setCredentialsMatcher(matcher);

}

}

其他的几个类在这里不是重点,重要的是看这里面的逻辑。其中最重要的是doGetAuthorizationInfo和doGetAuthenticationInfo以及afterPropertiesSet这三个方法,doGetAuthorizationInfo是对当前的用户进行授权的,至于授权的时期,就是当用户需要验证的时候(框架进行回调),我这里只是简单的写死了,但是在实际项目开发中,我们一般会将权限存放在数据表中,所以真实情况是先到数据库中查出一个集合,然后迭代授权。

doGetAuthenticationInfo对于的是对用户验证,主要的一个点在于我们最后返回的那个SimpleAuthenticationInfo,这个是加密的策略,这里的密码是密文的(根据loginName数据中取得),下面是密码的加密策略:

//为明文密码加密

publicString encryptionPassword(String plainPassword){byte[] salt =Digests.generateSalt(SALT_SIZE); //SALT_SIZE=8byte[] hashPassword =Digests.sha1(plainPassword.getBytes(), salt, HASH_INTERATIONS); //HASH_INTERATIONS=1024

return Encodes.encodeHex(salt)+Encodes.encodeHex(hashPassword); }

我这里是生成了了16位的salt,然后用来加密明文,最后两个加起来存入到数据中。根据上面说的,所以看到我们doGetAuthenticationInfo返回的是分开的两部分。这里也需要注意,这个密码最后的校验我们做的,而是框架!我们只是提供了校验类供它回调(下面我们使用的是默认的校验类,我们也可以自定义):

//在bean初始化完成以后 设置校验的规则

public void afterPropertiesSet() throwsException {

HashedCredentialsMatcher matcher= newHashedCredentialsMatcher(UserService.HASH_ALGORITHM);

matcher.setHashIterations(UserService.HASH_INTERATIONS);

setCredentialsMatcher(matcher);

,那么在密码进行验证的时候,就会调用HashedCredentialsMatcher的

@Overridepublic booleandoCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {

Object tokenHashedCredentials=hashProvidedCredentials(token, info);

Object accountCredentials=getCredentials(info);returnequals(tokenHashedCredentials, accountCredentials);

}

方法,这个Info就是我们前面方法doGetAuthenticationInfo提供的,至于另外的一个Token,后面会讲(也是一个方法提供的)。

4

下面就是讲解SessionManager,因为Shiro有自己的一套session体系,有sessionManager就不奇怪了,sessionManager主要职责是管理session的创建和删除,特别提一下,sessionManager对session的操作,其实只是调用了sessionDAO,然再加上自己的一些操作。

看源码:

public class DefaultSessionManager extends AbstractValidatingSessionManager implementsCacheManagerAware {//TODO - complete JavaDoc

private static final Logger log = LoggerFactory.getLogger(DefaultSessionManager.class);privateSessionFactory sessionFactory;protected SessionDAO sessionDAO; //todo - move SessionDAO up to AbstractValidatingSessionManager?

privateCacheManager cacheManager;private booleandeleteInvalidSessions;publicDefaultSessionManager() {this.deleteInvalidSessions = true;this.sessionFactory = newSimpleSessionFactory();this.sessionDAO = newMemorySessionDAO();

}

.......

protected void create(Session session) {

if (log.isDebugEnabled()) {

log.debug("Creating new EIS record for new session instance [" + session + "]");

}

sessionDAO.create(session);

}

这上面的源码中就知道SessionManager就是对SessionDAO进行了代理的作用。

我们就明白了sessionManager依赖sessionDAO(后面实现自己的SessionDAO需要注入到SessionManager中),下面是自己实现的sessionManager:

packagecom.yonyou.hotusm.common.security.session;importorg.apache.shiro.session.InvalidSessionException;importorg.apache.shiro.session.Session;importorg.apache.shiro.session.UnknownSessionException;importorg.apache.shiro.session.mgt.SessionContext;importorg.apache.shiro.session.mgt.SessionKey;importorg.apache.shiro.session.mgt.SimpleSession;importorg.apache.shiro.web.servlet.Cookie;importorg.apache.shiro.web.servlet.ShiroHttpServletRequest;importorg.apache.shiro.web.servlet.SimpleCookie;importorg.apache.shiro.web.session.mgt.DefaultWebSessionManager;importorg.apache.shiro.web.util.WebUtils;importjavax.servlet.ServletRequest;importjavax.servlet.ServletResponse;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.Serializable;importjava.util.Collection;importjava.util.Date;/***

*

*@authorHotusm

* v2015-11-04*/

public class SessionManager extendsDefaultWebSessionManager{/**DefaultWebSessionManager 实现了DefaultSessionManager的功能 并在其上实现了web的功能

* 也就是在上面实现了将SessionId 存到了Cookie中

**/@OverrideprotectedSerializable getSessionId(ServletRequest request,

ServletResponse response) {

String sid=request.getParameter("_sid");if(org.apache.commons.lang.StringUtils.isNotBlank(sid)){if(WebUtils.isTrue(request, "_cookie")){

HttpServletRequest req=(HttpServletRequest) request;

HttpServletResponse resp=(HttpServletResponse) response;

Cookie template=getSessionIdCookie();

Cookie cookie=newSimpleCookie(template);

cookie.setValue(sid);

cookie.saveTo(req, resp);

}

request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,ShiroHttpServletRequest.URL_SESSION_ID_SOURCE);

request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sid);

request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);returnsid;

}return super.getSessionId(request, response);

}

@OverrideprotectedSession doCreateSession(SessionContext context) {try{return super.doCreateSession(context);

}catch(Exception e) {return null;

}

}

@OverrideprotectedSession newSessionInstance(SessionContext context) {

Session session=super.newSessionInstance(context);

session.setTimeout(getGlobalSessionTimeout());returnsession;

}

@OverrideprotectedSession retrieveSession(SessionKey sessionKey)throwsUnknownSessionException {try{return super.retrieveSession(sessionKey);

}catch(Exception e) {//获取不到SESSION不报错

return null;

}

}

@Overridepublic voidvalidateSessions() {super.validateSessions();

}

@OverridepublicSession start(SessionContext context) {try{return super.start(context);

}catch(Exception e) {

SimpleSession session=newSimpleSession();

session.setId(0);returnsession;

}

}

@OverridepublicDate getStartTimestamp(SessionKey key) {try{return super.getStartTimestamp(key);

}catch(Exception e) {return null;

}

}

@OverridepublicDate getLastAccessTime(SessionKey key) {try{return super.getLastAccessTime(key);

}catch(Exception e) {return null;

}

}

@Overridepublic long getTimeout(SessionKey key) throwsInvalidSessionException {try{return super.getTimeout(key);

}catch(Exception e) {return 0;

}

}

@Overridepublic void setTimeout(SessionKey key, longmaxIdleTimeInMillis)throwsInvalidSessionException {try{super.setTimeout(key, maxIdleTimeInMillis);

}catch(Exception e) {

}

}

@Overridepublic void touch(SessionKey key) throwsInvalidSessionException {try{super.touch(key);

}catch(Exception e) {

}

}

@OverridepublicString getHost(SessionKey key) {try{return super.getHost(key);

}catch(Exception e) {return null;

}

}

@Overridepublic CollectiongetAttributeKeys(SessionKey key) {try{return super.getAttributeKeys(key);

}catch(Exception e) {return null;

}

}

@OverridepublicObject getAttribute(SessionKey sessionKey, Object attributeKey)throwsInvalidSessionException {try{return super.getAttribute(sessionKey, attributeKey);

}catch(Exception e) {return null;

}

}

@OverridepublicObject removeAttribute(SessionKey sessionKey, Object attributeKey)throwsInvalidSessionException {try{return super.removeAttribute(sessionKey, attributeKey);

}catch(Exception e) {return null;

}//

}

@Overridepublic void stop(SessionKey key) throwsInvalidSessionException {try{super.stop(key);

}catch(Exception e) {

}

}

@Overridepublic void checkValid(SessionKey key) throwsInvalidSessionException {try{super.checkValid(key);

}catch(Exception e) {

}

}

}

上面就是对session的操作.

5

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

packagecom.yonyou.hotusm.common.security.session;importcom.google.common.collect.Sets;importcom.yonyou.hotusm.common.config.Global;importcom.yonyou.hotusm.common.web.Servlets;importorg.apache.shiro.session.Session;importorg.apache.shiro.session.UnknownSessionException;importorg.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;importjavax.servlet.http.HttpServletRequest;importjava.io.Serializable;importjava.util.Collection;importjava.util.Set;/***@authorHotusm

* v-2015-10-28*/

public class CacheSessionDAO extends EnterpriseCacheSessionDAO implementsSessionDAO {

@OverrideprotectedSerializable doCreate(Session session) {

HttpServletRequest request=Servlets.getRequest();if (request != null) {

String uri=request.getRequestURI();if(Servlets.isStaticFile(uri)) {return null;

}

}super.doCreate(session);//System.out.println("doCreate:"+" sessionId"+session.getId());

returnsession.getId();

}

@Overridepublic Session readSession(Serializable sessionId) throwsUnknownSessionException {//System.out.println("readSession:"+" sessionId"+sessionId);//System.out.println();

try{

Session s= null;

HttpServletRequest request=Servlets.getRequest();if (request != null) {

String uri=request.getRequestURI();if(Servlets.isStaticFile(uri)) {return null;

}

s= (Session) request.getAttribute("session_" +sessionId);

}if (s != null) {returns;

}

Session session= super.readSession(sessionId);if (request != null && session != null) {

request.setAttribute("session_" +sessionId, session);

}returnsession;

}catch(Exception e) {return null;

}

}

@OverrideprotectedSession doReadSession(Serializable sessionId) {//System.out.println("doReadSession:"+" sessionId"+sessionId);

return super.doReadSession(sessionId);

}

@Overrideprotected voiddoUpdate(Session session) {//System.out.println("doUpdate"+" sessionId"+session.getId());

if (session == null || session.getId() == null) {return;

}

HttpServletRequest request=Servlets.getRequest();if (request != null) {

String uri=request.getRequestURI();if(Servlets.isStaticFile(uri)) {return;

}if (org.apache.commons.lang.StringUtils.startsWith(uri, Global.getConfig("web.view.prefix"))&& org.apache.commons.lang.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);

}

@Overrideprotected voiddoDelete(Session session) {//System.out.println("doDelete");

if (session == null || session.getId() == null) {return;

}super.doUpdate(session);

}public Collection getActiveSessions(booleanincludeLeave) {return null;

}public Collection getActiveSessions(booleanincludeLeave,

Object principal, Session filterSession) {if (includeLeave && principal == null) {return this.getActiveSessions();

}

Set sessions =Sets.newHashSet();for(Session session : getActiveSessions()) {boolean isActiveSession = true;

}return null;

}

}

6.

看sessionDAO还有一个idGen依赖bean,指的是sessionId值的生成策略,这个bean也是自己定义的,但是需要继承SessionIdGenerator:

public class IdGen implementsSessionIdGenerator{private staticSecureRandom secureRandom;/*** 封装JDK自带的UUID,通过random生成*/

public staticString uuid(){return UUID.randomUUID().toString().replace("-", "");

}public static longrandomLong(){returnMath.abs(secureRandom.nextLong());

}publicSerializable generateId(Session session) {returnIdGen.uuid();

}

}

返回的就是session的值,至于shiroCacheManager就是session缓存的储存位置(它依赖的是我们在spring-context定义的cacheManager)。

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

/*** 登录成功,进入管理首页*/@RequiresPermissions("user")

@RequestMapping(value= "${adminPath}")publicString 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的代码,

@Servicepublic class FormAuthenticationFilter extendsorg.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;@OverrideprotectedAuthenticationToken createToken(ServletRequest request, ServletResponse response) {

String username=getUsername(request);

String password=getPassword(request);boolean rememberMe =isRememberMe(request);

String host =StringUtils.getRemoteAddr((HttpServletRequest)request);boolean mobile =isMobileLogin(request);

return newUsernamePasswordToken(username, password.toCharArray(), rememberMe, host, mobile);//end

}publicString getCaptchaParam() {returncaptchaParam;

}protectedString getCaptcha(ServletRequest request) {returnWebUtils.getCleanParam(request, getCaptchaParam());

}publicString getMobileLoginParam() {returnmobileLoginParam;

}protected booleanisMobileLogin(ServletRequest request) {returnWebUtils.isTrue(request, getMobileLoginParam());

}publicString getMessageParam() {returnmessageParam;

}/*** 登录成功之后跳转URL*/@OverridepublicString getSuccessUrl() {return super.getSuccessUrl();

}

@Overrideprotected voidissueSuccessRedirect(ServletRequest request,

ServletResponse response)throwsException {//Principal p = UserUtils.getPrincipal();//if (p != null && !p.isMobileLogin()){

WebUtils.issueRedirect(request, response, getSuccessUrl(), null, true);//}else{//super.issueSuccessRedirect(request, response);//}

}/*** 登录失败调用事件*/@Overrideprotected booleanonLoginFailure(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;

}

}

这里的Token就是我们前面所讲的Info一起来做明文和密文进行校验的。

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

具体的代码在我github:https://github.com/Housum/blog.git 有

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值