spring_security安全框架详解

本文是在项目中用到Spring Security3来进行登录验证时才进行学习的,写的比较片面,请大家提出问题。

优秀的安全框架连接:

http://blog.csdn.net/z69183787/article/details/38542109

http://blog.csdn.net/z69183787/article/details/38542125

http://blog.csdn.net/z69183787/article/details/38542139

首先要在web.xml中配置

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:/applicationContext.xml
classpath*:/applicationContext-security.xml
<!--classpath*:/applicationContext-timer.xml 
--></param-value>
</context-param>

加载security配置文件

加入过滤器:

<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
<filter-mapping>
<span style="white-space:pre">		</span><filter-name>springSecurityFilterChain</filter-name>
<span style="white-space:pre">		</span><url-pattern>/*</url-pattern>
<span style="white-space:pre">		</span><dispatcher>REQUEST</dispatcher>
<span style="white-space:pre">		</span><dispatcher>FORWARD</dispatcher>
<span style="white-space:pre">	</span></filter-mapping>

session监听器:

 <listener> 
	    <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class> 
    </listener> 


用户是怎样认证的?

在我们的安全系统中,当一个用户在我们的登录form中提供凭证后,这些凭证信息必须与凭证存储中的数据进行校验以确定下一步的行为,凭证的校验涉及到一系列的逻辑组件,它们封装了认证过程。

站在一个较高的层次上看,你可以看到有三个主要的组件负责这项重要的事情:

接口名                                         描述/角色

AbstractAuthenticationProcessingFilter 它在基于web的认证请求中使用。处理包含认证信息的请求,如认     证信息可能是form POST提交的, SSO信息或者其他用户提供的。创 建一个部分完整的 Authentication对象以在链中 传递凭证信息。

 

AuthenticationManager 它用来校验用户的凭证信息,或 者会抛出一个特定的异常(校验 失败的情况)或者完整填充 Authentication象,将会包含 了权限信息。

 

AuthenticationProvider 它为AuthenticationManager 提供凭证校验。一些 AuthenticationProvider的实 现基于凭证信息的存储,如数 库,来判定凭证信息是否可以被 认可。


spring_security_login是什么?我们怎么达到这个页面的?

URLspring_security_login部分表明这是一个默认的登录页面并且是在DefaultLoginPageGeneratingFilter中命名的,我们可以使用配置属性来修改这个页面的名字从而使它对我们应用来说是唯一的。

建议修改登录页URL的默认值,修改后不仅能够对应用或搜索引擎更友好,而且能够隐藏你使用Spring Security做为安全实现的事实。

下面就是一段登录页面login.jsp的代码:

<!-- j_spring_security_check
		并不对应任何应用中的物理资源。它只是UsernamePasswordAuthenticationFilter监视的一个基于form登录的URL 
		
		文本域的名字是UsernamePasswordAuthenticationFilter规定的,并借鉴了Java EE Servlet 2.x的规范
		规范要求登录的form使用特定的名字并且form的action要为特定的j_security_check
		值。这样的实际模式目标是允许基于Java EE servlet-based的应用能够与servlet容器的安全设施以标准的方式
		连接起来。
-->
<form id="loginForm" action="${ctx}/j_spring_security_check" method="post">
				<div style="height:40px;line-height:40px"> <input class="input_users" id="j_username" name="j_username" type="text" /></div>
				<div style="height:49px;line-height:49px"> <input class="input_users" id="j_password" name="j_password" type="password" /></div>
				<div style="height:60px;line-height:60px"> <input type="button" name="login"  class="input_login" οnfοcus="this.blur()" οnclick="check();"/></div>
			</form>

这是一个利用spring安全框架登录的页面,点击提交后,会检查用户名和密码是否为所需正则,再进行提交,提交会根据配置文件进行认证匹配

 

下面给出application-security.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:s="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd"
	default-lazy-init="true">

	<description>SpringSecurity安全配置</description>
	
	<!-- http安全配置 -->
	<!-- auto-config设为true会自动提供以下3个认证相关的功能
		1、HTTP基本认证
		2、Form登录认证
		3、退出
		你也可以使用配置元素来实现这三个功能,比使用auto-config提供的功能更精确
	 -->
	<s:http auto-config="true" use-expressions="true" >
		<s:intercept-url pattern="/css/**" filters="none" />
		<s:intercept-url pattern="/images/**" filters="none" />
		<s:intercept-url pattern="/js/**" filters="none" />
		<s:form-login login-page="/login.action"
			default-target-url="/main.html" authentication-failure-url="/login.action?error=true" />
		<!-- logout-success-url退出指向的路径   
			 invalidate-session是否让session失效
			 logout-url退出的路径,默认不写为j_spring_security_logout
			 success-handler-ref 对一个LogoutSuccessHandler实现的引用。
	    -->
		<s:logout logout-success-url="/login.action" invalidate-session="true"/>
		<!-- 
			Session固化是恶意用户试图窃取系统中一个未认证用户的session。对攻击者来说,可以通过各种技术来
			获取用户session的唯一标识(例如,JSESSIONID)。如果攻击者创建了带有用户JSESSIONID的cookie或者
			URL参数,他就能够访问用户的session。
			session-fixation-protection有三个值:
			none:使得session固化攻击失效,不会配置SessionManagementFilter(除非其它的
				<session-management>属性不是默认值)
			migrateSession(默认):当用户经过认证后分配一个新的session,它保证原session的所有属性移到
				新session中。我们将在后面的章节中讲解,通过基于bean的方式如何进行这样的配置。
			newSession:当用户认证后,建立一个新的session,原(未认证时)session的属性不会进行移到新session中来。
		 -->
		 
		 <!-- 
		 	invalid-session-url:是指session过期后重定向到的url地址
		  -->
		<s:session-management>
			<!-- 你不想使用session并发控制,你可以可以这样做。只需将max-sessions的值设置为-1,这样
				session跟踪会保持可用,但没有最大session个数的限制。
				expired-url:用户再次登录重定向的地址
			 -->
			<s:concurrency-control max-sessions="1" error-if-maximum-exceeded="false" expired-url="/message.jsp"/>
		</s:session-management>
	</s:http>
	<!-- 认证配置, 使用userDetailsService提供的用户信息 -->
	<s:authentication-manager alias="authenticationManager">
		<s:authentication-provider user-service-ref="userDetailsService">
			<!-- 对密码进行加密 -->
			<s:password-encoder hash="md5" />
			<s:password-encoder hash="plaintext">
			</s:password-encoder>
		</s:authentication-provider>
	</s:authentication-manager>

	<!-- 项目实现的用户查询服务 -->
	<bean id="userDetailsService" class="com.wiseweb.pom.service.account.UserDetailsServiceImpl" />
</beans>


 

认证配置中的authentication-manager是对用户输入的信息进行验证的操作,我们通过实现UserDetailService进行实现。

下面是UserDetailServiceImpl代码图


 package com.wiseweb.pom.service.account;
 
 import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.wiseweb.pom.entity.Role;
import com.wiseweb.pom.entity.Menu;
import com.wiseweb.util.ElintUtil;
import com.wiseweb.util.LoginUser;

import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.transaction.annotation.Transactional;
 
 @Transactional(readOnly=false)
 public class UserDetailsServiceImpl
   implements UserDetailsService {
   private AccountManager accountManager;
 
   public UserDetails loadUserByUsername(String username)
     throws UsernameNotFoundException, DataAccessException{
     com.wiseweb.pom.entity.User user = this.accountManager.findUniqueUser(username);
     if (user == null) {
    	 throw new UsernameNotFoundException("用户" + username + " 不存在");
     }
     @SuppressWarnings("rawtypes")
	Set grantedAuths = obtainGrantedAuthorities(user);
     boolean enabled = true;//是不是激活的
     boolean accountNonExpired = true;//账户是否过期
     boolean credentialsNonExpired = true;//认证是否过期
     boolean accountNonLocked = true;//是否锁定
 
     @SuppressWarnings("unchecked")
	UserDetails userdetails = new org.springframework.security.core.userdetails.User(
       user.getLoginName(), user.getPassWord(), enabled, 
       accountNonExpired, credentialsNonExpired, accountNonLocked, 
       grantedAuths);
 
     return new LoginUser(userdetails, user);
   }
 
   @SuppressWarnings("unchecked")
private Set<GrantedAuthority> obtainGrantedAuthorities(com.wiseweb.pom.entity.User user)
   {
     @SuppressWarnings("rawtypes")
	Set authSet = Sets.newHashSet();
     
     List<Role>  roles = user.getRoles();
     Set<Menu> menus = Sets.newHashSet();
     for(Role role :roles){
    	 System.out.println(role.getRoleName());
    	 menus.addAll(role.getMenus());
     }
     List<Menu> menusList = Lists.newArrayList() ;
     menusList.addAll(menus);
     user.setMenus(menusList);
     for (Menu menu : user.getMenus()) {
       if (menu.getNodeType().intValue() == 3) {
         authSet.add(new GrantedAuthorityImpl(menu.getPrefixedName()));
       }
     }
     user.setMenus(new ElintUtil().menuRelationSet(user.getMenus()));
     return authSet;
   }
 
   @Autowired
   public void setAccountManager(AccountManager accountManager) {
     this.accountManager = accountManager;
   }
 }

这个类通过实现UserDetailService来达到返回一个org.springframework.security.core.userdetails.UserDetails的目的。

下面是我的User实体类:

 package com.wiseweb.pom.entity;
 
 import com.google.common.collect.Lists;
import com.wiseweb.pom.entity.IdEntity;
import com.wiseweb.pom.entity.Menu;
import com.wiseweb.pom.entity.Department;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

 import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
 
 @SuppressWarnings("unused")
@Entity
 @Table(name="wise_user")
 public class User extends IdEntity
 {
   private static final long serialVersionUID = 1L;
   private String loginName;
   private String name;
   private String passWord;
   private String userStatus;
   private String email;
   private String telephone;
   private Integer sex;
   private Date lastLoginTime ;
   private String userSign ;
   private Integer orderType ;
   private List<Role> roles = Lists.newArrayList() ;
   private Integer flag ;
	private List<Menu> menus = Lists.newArrayList();
	private List<HomeMenu> homeMenus = new ArrayList<HomeMenu>() ;
   private Department department = null;
 
   public User()
   {
   }
 
   public User(String loginName, String name, String passWord, String userStatus, String email, String telephone)
   {
     this.loginName = loginName;
     this.name = name;
     this.passWord = passWord;
     this.userStatus = userStatus;
     this.email = email;
     this.telephone = telephone;
   }
   public User(String loginName, String name, String passWord, String userStatus, String email, String telephone,Integer sex)
   {
     this.loginName = loginName;
     this.name = name;
     this.passWord = passWord;
     this.userStatus = userStatus;
     this.email = email;
     this.telephone = telephone;
     this.sex = sex;
   }
   @Column(name="login_name", nullable=false, length=45)
   public String getLoginName() {
     return this.loginName;
   }
 
   public void setLoginName(String loginName) {
     this.loginName = loginName;
   }
 
   @Column(name="name", nullable=false, length=45)
   public String getName() {
     return this.name;
   }
 
   public void setName(String name) {
     this.name = name;
   }
 
   @Column(name="pass_word", nullable=false, length=45)
   public String getPassWord() {
     return this.passWord;
   }
 
   public void setPassWord(String passWord) {
     this.passWord = passWord;
   }
 
   @Column(name="user_status", nullable=false, length=45)
   public String getUserStatus() {
	   if(this.userStatus != null){
		   return this.userStatus;
	   }else{
		   return "1";
	   }
   }
 
   public void setUserStatus(String userStatus) {
     this.userStatus = userStatus;
   }
 
   @Column(name="email", length=45)
   public String getEmail() {
     return this.email;
   }
 
   public void setEmail(String email) {
     this.email = email;
   }
 
   @Column(name="telephone", length=45)
   public String getTelephone() {
     return this.telephone;
   }
 
   public void setTelephone(String telephone) {
     this.telephone = telephone;
   }
   
   @Column(name="sex")
	public Integer getSex() {
	   if(this.sex != null){
		   return sex;
	   }else{
		   return 1;
	   }
	}
	
	public void setSex(Integer sex) {
		this.sex = sex;
	}
   public void setMenus(List<Menu> menus) {
     this.menus = menus;
   }
 
   @Transient
   public List<Menu> getMenus() {
     return this.menus;
   }
   
   @Transient
   public List<HomeMenu> getHomeMenus() {
	return this.homeMenus;
   }

   public void setHomeMenus(List<HomeMenu> homeMenus) {
	this.homeMenus = homeMenus;
   }

@Column(name="last_login_time", nullable=false, length=45)
   public Date getLastLoginTime() {
	return lastLoginTime;
	}
	
	public void setLastLoginTime(Date lastLoginTime) {
		this.lastLoginTime = lastLoginTime;
	}
	@Column(name="user_sign", length=45)
	public String getUserSign() {
		return userSign;
	}
	
	public void setUserSign(String userSign) {
		this.userSign = userSign;
	}
	@Column(name="order_type")
	public Integer getOrderType() {
		return orderType;
	}
	public void setOrderType(Integer orderType) {
		this.orderType = orderType;
	}
	@Column(name="flag", nullable=false)
	public Integer getFlag() {
		return flag;
	}
	
	public void setFlag(Integer flag) {
		this.flag = flag;
	}
	@ManyToOne
	@JoinColumn(name="depart_id")
	public Department getDepartment() {
		return department;
	}

	public void setDepartment(Department department) {
		this.department = department;
	}
//	@ManyToMany
//	   @JoinTable(name="wise_role_user", joinColumns={@javax.persistence.JoinColumn(name="user_id")}, inverseJoinColumns={@javax.persistence.JoinColumn(name="role_id")})
//	   @Fetch(FetchMode.SUBSELECT)
//	   @Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
	   
	@ManyToMany
	@JoinTable(name = "wise_role_user", joinColumns = { @javax.persistence.JoinColumn(name = "user_id") }, inverseJoinColumns = { @javax.persistence.JoinColumn(name = "role_id")})
	@Fetch(FetchMode.SUBSELECT)
	@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
	public List<Role> getRoles() {
		return roles;
	}

	public void setRoles(List<Role> roles) {
		this.roles = roles;
	}
	
 }

有人可能要问了?为什么userDetails返回的是一个new LoginUser(userdetails, user)呢?

我们来看看LoginUser代码

 package com.wiseweb.util;
 
 import com.wiseweb.pom.entity.HomeMenu;
import com.wiseweb.pom.entity.Role;
import com.wiseweb.pom.entity.User;
import com.wiseweb.pom.entity.Menu;
import com.wiseweb.pom.entity.Department;

 import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
 
 public class LoginUser implements UserDetails{
	 
   private static final long serialVersionUID = 1366695319372075545L;
   private UserDetails userDetails;
   private User user;
 
   public LoginUser(UserDetails userDetails, User user)
   {
     this.userDetails = userDetails;
     this.user = user;
   }
 
   public Collection<GrantedAuthority> getAuthorities()
   {
     return this.userDetails.getAuthorities();
   }
 
   public String getPassword()
   {
     return this.userDetails.getPassword();
   }
 
   public String getUsername()
   {
     return this.userDetails.getUsername();
   }
 
   public boolean isAccountNonExpired()
   {
     return this.userDetails.isAccountNonExpired();
   }
 
   public boolean isAccountNonLocked()
   {
     return this.userDetails.isAccountNonLocked();
   }
 
   public boolean isCredentialsNonExpired()
   {
     return this.userDetails.isCredentialsNonExpired();
   }
 
   public boolean isEnabled()
   {
     return this.userDetails.isAccountNonExpired();
   }
 
   public UserDetails getUserDetails() {
     return this.userDetails;
   }
 
   public String getLoginName() {
     return this.user.getLoginName();
   }
 
   public String getName() {
     return this.user.getName();
   }
   //by lib
   public Long getUserId() {
	     return this.user.getId();
   }
 
   public String getPassWord() {
     return this.user.getPassWord();
   }
 
   public String getUserStatus() {
     return this.user.getUserStatus();
   }
 
   public String getEmail() {
     return this.user.getEmail();
   }
 
   public String getTelephone() {
     return this.user.getTelephone();
   }
 
   public List<Menu> getMenus() {
     return this.user.getMenus();
   }
 
   public Long getId() {
     return this.user.getId();
   }
   public Integer getSex(){
	   return this.user.getSex() ;
   }
   public Date lastLoginName(){
	   return this.user.getLastLoginTime() ;
   }
   public String userSign(){
	   return this.user.getUserSign() ;
   }
   public Integer getOrderType(){
	   return this.user.getOrderType() ;
   }
   public List<Role> getRoles(){
	   return this.user.getRoles() ;
   }
   public Integer getFlag(){
	   return this.user.getFlag() ;
   }
   public Department getDepartment(){
	   return this.user.getDepartment() ;
   }
   public List<HomeMenu> getHomeMenus(){
	   return this.user.getHomeMenus() ;
   }
   public User getUser() {
		return user;
	}

@Override
public int hashCode() {
	return this.userDetails.getUsername().hashCode() ;
}

@Override
public boolean equals(Object obj) {
	if(obj instanceof LoginUser){
		return this.userDetails.getUsername().equals(((LoginUser)obj).getUsername());
	}
	return false ;
}
}

这样如果用户的用户名和密码都正确后,spring security就会把信息放入userDetails中,并且创建一个名叫"SPRING_SECURITY_CONTEXT"的session,我们可以通过下面的方式拿到Login对象。

 LoginUser user = (LoginUser)((SecurityContext)
			   ServletActionContext.getRequest().getSession().getAttribute(
			   "SPRING_SECURITY_CONTEXT")).getAuthentication()
			   .getPrincipal();

这样我们就可以在代码中拿到user对象进行一系列操作了。


下面我要讲一下项目中实现单点登录(一方登录,另一方再用同一帐号登录时,第一个用户被顶)的实现

我做单点登录的时候,网上很多教程很片面的说了一下spring security的配置,例如下面的配置:

<s:session-management>
			<s:concurrency-control max-sessions="1" error-if-maximum-exceeded="false" expired-url="/message.jsp"/>
		</s:session-management>

可以这个配置好了之后,还是不起作用的,最根本的要去重写hashCode和equals方法,为什么呢?

因为第一个用户登录了之后,是把用户信息(帐号密码)放在userDetails中的,第二个用户再进行登录后,他的信息又会放入userDetails中,这时候就要在放入的时候进行比较,如果相同则告诉spring容器有相同的帐号登录了,就请求spring踢除第一个用户。

所以我们必须比较userDetails中的username,那么在哪里比较呢,我这个例子是在LoginUser中比较的,当然大家实现的不一样,原理其实都一样。

下面是最核心的一段代码:

@Override
public int hashCode() {
	return this.userDetails.getUsername().hashCode() ;
}

@Override
public boolean equals(Object obj) {
	if(obj instanceof LoginUser){
		return this.userDetails.getUsername().equals(((LoginUser)obj).getUsername());
	}
	return false ;
}

接着又来了一个问题,我要做成像腾讯qq那样,重复登录后及时被顶并且提示“您的账户已在异地登录”之类的提示语,告诉用户

下面是我一开始的配置

<s:session-management invalid-session-url="login.jsp">
			<s:concurrency-control max-sessions="1" error-if-maximum-exceeded="false" expired-url="/message.jsp"/>
		</s:session-management>

这是message.jsp代码

<%@ page contentType="text/html;charset=UTF-8" %>
<%@ include file="/common/taglibs.jsp" %>
<%@ page import="org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter" %>
<%@ page import="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter" %>
<%@ page import="org.springframework.security.web.WebAttributes" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!--框架必需start-->
<script type="text/javascript" src="${ctx }/js/jquery-1.4.js"></script>
<script type="text/javascript" src="${ctx }/js/framework.js"></script>
<script type="text/javascript" src="${ctx }/js/attention/messager.js"></script>
<link href="${ctx }/css/import_basic.css" rel="stylesheet" type="text/css"/>
<link  rel="stylesheet" type="text/css" id="skin" prePath="${ctx }/"//>
<!--框架必需end-->
<!-- 系统必须 start-->
<script type="text/javascript" src="${ctx}/js/table.js"></script>
<script type="text/javascript" src="${ctx}/js/table/treeTable.js"></script>
<script type="text/javascript" src="${ctx}/js/elint.js"></script>
<!-- 系统必须 end-->
<!--截取文字start-->
<script type="text/javascript" src="${ctx }/js/text/text-overflow.js"></script>
<script type='text/javascript'>
			$(function(){
				top.Dialog.alert('您的帐号已在其他地方登录,您当前的帐号已退出!',function (){
					location='<%=path%>/login.jsp' ;
				});
			});
		</script>
</head>
<body>
</body>
</html>


可是每次被顶后,用户都直接跳到login.jsp去了,而没有走message.jsp,我就很苦恼。

于是我看了一下SessionmanagerFilter和ConcurrentSessionFilter的源码,认为我的配置是正确的。

下面是两个源码,也可以去我的博客去看全文:http://blog.csdn.net/benjamin_whx/article/details/39204699

SessionManagerFilter:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)  
        throws IOException, ServletException {  
    HttpServletRequest request = (HttpServletRequest) req;  
    HttpServletResponse response = (HttpServletResponse) res;  
    //省略……  
     //判断当前session中是否有SPRING_SECURITY_CONTEXT属性  
    if (!securityContextRepository.containsContext(request)) {  
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();  
  
        if (authentication != null && !authenticationTrustResolver.isAnonymous(authentication)) {  
            try {  
                //再通过sessionStrategy执行session固化、并发处理  
                   //与UsernamePasswordAuthenticationFilter时处理一样,后面会仔细分析。  
                sessionStrategy.onAuthentication(authentication, request, response);  
            } catch (SessionAuthenticationException e) {  
                SecurityContextHolder.clearContext();  
                failureHandler.onAuthenticationFailure(request, response, e);  
                return;  
            }  
            //把SecurityContext设置到当前session中  
            securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response);  
        } else {  
            if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {  
                if (invalidSessionUrl != null) {  
                    request.getSession();  
                    redirectStrategy.sendRedirect(request, response, invalidSessionUrl);  
  
                    return;  
                }  
            }  
        }  
    }  
  
    chain.doFilter(request, response);  
}  

ConcurrentSessionFilter

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)  
        throws IOException, ServletException {  
    HttpServletRequest request = (HttpServletRequest) req;  
    HttpServletResponse response = (HttpServletResponse) res;  
  
    HttpSession session = request.getSession(false);  
    if (session != null) {  
        //这个SessionInformation是在执行SessionManagementFilter时通过sessionRegistry构造的并且放置在map集合中的  
        SessionInformation info = sessionRegistry.getSessionInformation(session.getId());  
        //如果当前session已经注册了  
        if (info != null) {  
            //如果当前session失效了  
            if (info.isExpired()) {  
                // Expired - abort processing  
                //强制退出  
                doLogout(request, response);  
                //目标url为expired-url标签配置的属性值  
                String targetUrl = determineExpiredUrl(request, info);  
                //跳转到指定url  
                if (targetUrl != null) {  
                    redirectStrategy.sendRedirect(request, response, targetUrl);  
  
                    return;  
                } else {  
                    response.getWriter().print("This session has been expired (possibly due to multiple concurrent " +  
                            "logins being attempted as the same user).");  
                    response.flushBuffer();  
                }  
  
                return;  
            } else {  
                // Non-expired - update last request date/time  
                //session未失效,刷新时间  
                info.refreshLastRequest();  
            }  
        }  
    }  
  
    chain.doFilter(request, response);  
}  

这一句话的意思就是没有指定expired-url就会默认写出这么一段话
response.getWriter().print("This session has been expired (possibly due to multiple concurrent " +  
                            "logins being attempted as the same user)."); 

可是我还是没有走message.jsp,带着疑问我把session-manager中的invalid-session-url="login.jsp"去掉了,这样就可以了,是不是spring先判断走SessionManagerFilter?如果session为空就直接根据invalid-session-url跳走了呢?希望有人给我指出。



  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值