需求是已经登录的用户如果被管理员注销权限,则在网页上的任何下一步操作都会被强制退出,需要重新登录,
并且在系统中同一用户不能重复登录。
实现思路:在网页上的操作分为两种,一种是页面请求,一种是AJAX请求,需要对这两种情况进行分别拦截。首先在用户登录成功后,将该用户的sessionid存入数据库中,之后配置拦截器对所有请求进行拦截,进行session和用户有效性判断,如果当前sessionid和数据库中存储的sessionid相同,则认为用户登录有效,通过拦截器;如果当前sessionid雨数据库中存储的不相同,则认为有重复登录情况,将当前sessionid销毁,跳转到登录界面。
在security配置文件中加入登录成功后进入的处理类
<form-login
login-processing-url="/resources/j_spring_security_check"
login-page="/login"
authentication-failure-url="/login?login_error=t"
authentication-success-handler-ref="authenticationSuccessHandler" />
<beans:bean id="authenticationSuccessHandler" class="com.sinoparasoft.spring.security.SimpleLoginSuccessHandler">
<beans:property name="defaultTargetUrl" value="/"></beans:property>
<beans:property name="forwardToDestination" value="true"></beans:property>
</beans:bean>
在处理类中设置将登陆后用户的session存入数据库中
package com.sinoparasoft.spring.security;
import java.io.IOException;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.sinoparasoft.model.Users;
import com.sinoparasoft.spring.security.tool.GetUserName;
public class SimpleLoginSuccessHandler implements AuthenticationSuccessHandler,InitializingBean {
private String defaultTargetUrl;
private boolean forwardToDestination = false;
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@Resource
GetUserName getusername;
@Transactional(readOnly=false,propagation= Propagation.REQUIRED,rollbackFor={Exception.class})
public void saveLoginInfo(HttpServletRequest request,Authentication authentication){
String username = getusername.username();//获取登录的用户名
String sessionid=request.getSession().getId();//获取登录的SESSIONID
Users user=Users.FindUserByUsername(username);
user.setSessionid(sessionid);
user.merge();//存入数据库中
}
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
}
public void setDefaultTargetUrl(String defaultTargetUrl) {
this.defaultTargetUrl = defaultTargetUrl;
}
public void setForwardToDestination(boolean forwardToDestination) {
this.forwardToDestination = forwardToDestination;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
// TODO Auto-generated method stub
this.saveLoginInfo(request, authentication);
if(this.forwardToDestination){
request.getRequestDispatcher(this.defaultTargetUrl).forward(request, response);
}else{
this.redirectStrategy.sendRedirect(request, response, this.defaultTargetUrl);
}
}
}
然后在springmvc配置文件中配置拦截器
<mvc:interceptors>
<!-- session超时 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="Interceptor.RequestInterceptor">
<property name="allowUrls">
<list>
<!-- 如果请求中包含以下路径,则不进行拦截 -->
<value>/resources</value>
<value>/login</value>
</list>
</property>
</bean>
</mvc:interceptor>
</mvc:interceptors>
package Interceptor;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import com.sinoparasoft.model.Users;
import com.sinoparasoft.spring.security.tool.GetUserName;
public class RequestInterceptor extends HandlerInterceptorAdapter {
@Resource
GetUserName getusername;
public String[] allowUrls;//还没发现可以直接配置不拦截的资源,所以在代码里面来排除
public void setAllowUrls(String[] allowUrls) {
this.allowUrls = allowUrls;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
}
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
// TODO Auto-generated method stub
request.setCharacterEncoding("UTF8");
HttpSession session=request.getSession();//获取登录的SESSION
String sessionid=request.getSession().getId();//获取登录的SESSIONID
String requestPath=request.getServletPath();//获取客户请求页面
String username = getusername.username();//获取登录的用户名
if(username.equals("admin"))
{
//默认初始超级管理员登录,不判断session
return true;
}
//先过滤掉不需要判断SESSION的请求
for(String url : allowUrls) {
if(requestPath.contains(url)) {
return true;
}
}
//判断用户名是否有效
if(Users.CheckUsernameExist(username)&&Users.CheckUserEnabled(username))
{
System.out.println("requestPath:"+requestPath);
System.out.println("允许登录");
//判断当前sessionid和数据库中的是否一样
Users user=Users.FindUserByUsername(username);
if(sessionid.equals(user.getSessionid()))
{
System.out.println("与数据库中sessionid相同,登录成功");
return true;
}
else
{
session.invalidate();
System.out.println("有其它用户登录强制退出,登录失败");
response.sendRedirect("sinoparasoft/");
return false;
}
}
else
{
System.out.println("不允许登录");
session.invalidate();
response.sendRedirect("sinoparasoft/");
return false;
}
}
}
以上基本完成需求,这些实现了页面跳转请求中的拦截,但是在网页中还有一些请求是ajax操作,当用户已经被强制下线后,他再点击ajax请求时,页面会没有任何响应,无法完成页面跳转到登录界面,所以需要在ajax请求完成之后增加complete判断,如果返回的不是success,就刷新当前页面,完成页面跳转请求,这样就可以被拦截器拦截,实现跳回登录页面的目的
$.ajax({
type: 'POST',
complete: function(XMLHttpRequest, textStatus) { if(textStatus!="success") location.reload(); },
url: '${userpwdchange}' ,
data: {userid:userid,username:username,pwd:userpwd},
dataType: 'json',
success: function(data){
alert("密码重置为:12345");
$('#userchange').modal('hide');
location.reload();
},
});