精品专区-精品自拍9-精品自拍三级乱伦-精品自拍视频-精品自拍视频曝光-精品自拍小视频

網(wǎng)站建設(shè)資訊

NEWS

網(wǎng)站建設(shè)資訊

SpringMVC中使用Interceptor+Cookie實(shí)現(xiàn)在一定天數(shù)之內(nèi)自動登錄

一 簡介

本篇文章主要介紹:在SpringMVC中如何使用Interceptor+Cookie實(shí)現(xiàn)在一定天數(shù)之內(nèi)自動登錄的功能。同時還介紹“如果校驗(yàn)失敗則跳轉(zhuǎn)到登錄頁面,在輸入用戶名、密碼等完成登錄之后又自動跳轉(zhuǎn)到原頁面”的功能實(shí)現(xiàn)

創(chuàng)新互聯(lián)從2013年創(chuàng)立,先為大同等服務(wù)建站,大同等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為大同企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。

本次測試環(huán)境是SSM框架,在正式介紹本篇文章之前,建議需要熟悉以下前置知識點(diǎn):

  • Mybatis中使用mybatis-generator結(jié)合Ant腳本快速自動生成Model、Mapper等文件(PS:這是為了快速生成一些基本文件)    https://www.zifangsky.cn/431.html

  • SpringMVC通過配置mvc:view-controller直接解析到視圖頁面(PS:這是為了簡化controller中的代碼)    https://www.zifangsky.cn/648.html

  • 基于SpringMVC的Cookie常用操作詳解(PS:這是介紹cookie的常用操作)    https://www.zifangsky.cn/665.html

  • SpringMVC中使用forward和redirect進(jìn)行轉(zhuǎn)發(fā)和重定向以及重定向時如何傳參詳解(PS:這是介紹重定向時如何傳參的問題)    https://www.zifangsky.cn/661.html

  • 在SpringMVC中使用攔截器(interceptor)攔截CSRF***(PS:這是介紹攔截器的一些基礎(chǔ)用法)    https://www.zifangsky.cn/671.html

二 代碼實(shí)現(xiàn)

(1)數(shù)據(jù)庫表設(shè)計:

我這里采用的是MySQL,同時設(shè)計了兩張表,分別是:user和persistent_logins

i)user表:

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL,
  `password` varchar(300) DEFAULT NULL,
  `email` varchar(64) DEFAULT NULL,
  `birthday` date DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'admin', 'admin', 'admin@zifangsky.cn', '2016-06-30');
INSERT INTO `user` VALUES ('2', 'test', '123456', 'test@zifangsky.cn', '2015-12-12');
INSERT INTO `user` VALUES ('3', 'zifangsky', 'zifangsky', 'zifangsky@zifangsky.cn', '2010-02-10');

這張表很簡單,就是一張普通的用戶表

ii)persistent_logins表:

DROP TABLE IF EXISTS `persistent_logins`;
CREATE TABLE `persistent_logins` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `series` varchar(300) DEFAULT NULL,
  `token` varchar(500) DEFAULT NULL,
  `validTime` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

這張表是用戶校驗(yàn)用戶自動登錄的表。設(shè)計這張表的原因是我看過一些網(wǎng)上的文章介紹使用cookie自動登錄,但是他們基本上都是將用戶名、密碼、salt等字符串拼接之后md5加密然后保存在cookie中。雖然使用了md5這類非對稱加密方式,但是將密碼這類關(guān)鍵信息保存在用戶端,我覺得是不太靠譜的。因此設(shè)計了這張表,將用戶名、密碼等關(guān)鍵信息加密之后的數(shù)據(jù)保存到這張表中,在用戶的cookie里只保存了沒有特殊含義的UUID值以及用戶名

這張表中的幾個字段的含義分別是:

  • id    主鍵

  • username    用戶名

  • series    用戶使用密碼登錄成功之后獲取的一個UUID值,同時用戶端保存的cookie記錄就是:EncryptionUtil.base64Encode(用戶名:此UUID值)

  • token    在攔截器中校驗(yàn)是否能夠登錄的密文,其加密方式是:EncryptionUtil.sha256Hex(用戶名 + “_” + 密碼 + “_” + 自動登錄失效的時間點(diǎn)的字符串 + “_” +  自定義的salt)

  • validTime    自動登錄失效的時間,即:這個時間點(diǎn)之后只能重新用用戶名、密碼登錄,如果在重新登錄時勾選了“30天內(nèi)自動登錄”則更新該用戶在persistent_logins這個表中的自動登錄記錄

(2)幾個基本的配置文件:

i)web.xml:



	
		contextConfigLocation
		
			classpath:context/context.xml
		
	
	
	
		org.springframework.web.context.ContextLoaderListener
	
	
		org.springframework.web.context.request.RequestContextListener
		
	
		springmvc
		org.springframework.web.servlet.DispatcherServlet
		
			contextConfigLocation
			classpath:context/springmvc-servlet.xml
		
		1
	

	
		springmvc
		*.html
	
	
	
		characterEncodingFilter
		org.springframework.web.filter.CharacterEncodingFilter
		
			encoding
			UTF-8
		
	
	
		characterEncodingFilter
		/*
	

ii)context.xml:




	
	

		
			com.mysql.jdbc.Driver
		
		
			jdbc:mysql://127.0.0.1:3306/cookie_db
		
		
			root
		
		
			root
		
		
		
			5
		
		
		
			30
		
		
		
			10
		
		
		
			60
		
		
		
			5
		
		
		
			0
		
		
		
			60
		
		
		
			30
		
		
		
			true
		
		
		
			false
		
	
	
	
		
		
	
	
		
		
	

	
		
	

	
	
		
	

	

iii)SpringMVC的配置文件springmvc-servlet.xml:



	
	
	
	
			
	
	
	
	
	
	
		
			
			
			
		
		
			
			
			
		
	
	
	
	
		
		
	

這里需要注意的是第31-35行的攔截器的配置,這個攔截器就是用于使用cookie自動登錄的攔截器,上面那個用于攔截登錄時的CSRF***的攔截器可以先不用管,直接注釋掉或者參考下我的這篇文章:https://www.zifangsky.cn/671.html

(3)Mapper層的代碼:

i)UserMapper:

package cn.zifangsky.mapper;

import cn.zifangsky.model.User;

public interface UserMapper {
    int deleteByPrimaryKey(Integer id);

    int insert(User record);

    int insertSelective(User record);

    User selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(User record);

    int updateByPrimaryKey(User record);
    
    /**
     * 根據(jù)用戶信息查用戶詳情(登錄)
     * */
    User selectByUser(User user);
    /**
     * 根據(jù)用戶名查用戶詳情
     * */
    User selectByName(String name);
}

這里除了使用插件自動生成的幾個方法之外,還添加了兩個其他的方法,它們對應(yīng)的SQL語句是:

  
    select 
    
    from user
    where name = #{name,jdbcType=VARCHAR}
  
    
  
    select 
    
    from user
    where name = #{name,jdbcType=VARCHAR} and password = #{password,jdbcType=VARCHAR}
      
        and email = #{email,jdbcType=VARCHAR}
      
      
        and birthday = #{birthday,jdbcType=DATE}
      
  

ii)PersistentLoginsMapper:

package cn.zifangsky.mapper;

import org.apache.ibatis.annotations.Param;

import cn.zifangsky.model.PersistentLogins;

public interface PersistentLoginsMapper {
	int deleteByPrimaryKey(Integer id);

	int insert(PersistentLogins record);

	int insertSelective(PersistentLogins record);

	PersistentLogins selectByPrimaryKey(Integer id);

	int updateByPrimaryKeySelective(PersistentLogins record);

	int updateByPrimaryKey(PersistentLogins record);

	/**
	 * 通過用戶名和UUID值查詢自動登錄記錄
	 * 
	 * @param username
	 *            用戶名
	 * @param series
	 *            UUID值
	 */
	PersistentLogins selectByUsernameAndSeries(@Param("username") String username, @Param("series") String series);

	/**
	 * 通過用戶名查詢自動登錄記錄
	 * 
	 * @param username
	 *            用戶名
	 */
	PersistentLogins selectByUsername(@Param("username") String username);
}

同樣,這里也添加了兩個其他的方法,它們對應(yīng)的SQL語句是:

  
    select 
    
    from persistent_logins where username = #{username,jdbcType=VARCHAR}
  
    
  
    select 
    
    from persistent_logins
    where username = #{username,jdbcType=VARCHAR} and series = #{series,jdbcType=VARCHAR}
  

(4)Manager層(即:業(yè)務(wù)邏輯層):

i)UserManager接口:

package cn.zifangsky.manager;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.zifangsky.model.User;

public interface UserManager {
	int deleteByPrimaryKey(Integer id);

	int insert(User user);

	int insertSelective(User user);

	User selectByPrimaryKey(Integer id);

	int updateByPrimaryKeySelective(User user);

	int updateByPrimaryKey(User user);
	
	/**
     * 根據(jù)用戶名查用戶詳情
     * */
    User selectByName(String name);
    
	/**
	 * 登錄
	 * 
	 * @param user
	 *            登錄的用戶信息
	 * @param rememberme
	 *            是否記住登錄
	 * @param response
	 *            HttpServletResponse
	 * @return 根據(jù)傳遞的用戶信息在數(shù)據(jù)庫中查詢到的用戶詳情
	 */
	User login(User user, boolean rememberme, HttpServletResponse response);
	
	/**
	 * 退出登錄
	 * */
	void logout(HttpServletRequest request,HttpServletResponse response);
}

ii)PersistentLoginsManager接口:

package cn.zifangsky.manager;

import cn.zifangsky.model.PersistentLogins;

public interface PersistentLoginsManager {
    int deleteByPrimaryKey(Integer id);

    int insert(PersistentLogins pLogins);

    int insertSelective(PersistentLogins pLogins);

    PersistentLogins selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(PersistentLogins pLogins);

    int updateByPrimaryKey(PersistentLogins pLogins);
    /**
	 * 通過用戶名和UUID值查詢自動登錄記錄
	 * 
	 * @param username
	 *            用戶名
	 * @param series
	 *            UUID值
	 */
	PersistentLogins selectByUsernameAndSeries(String username,String series);
	/**
	 * 通過用戶名查詢自動登錄記錄
	 * 
	 * @param username
	 *            用戶名
	 */
	PersistentLogins selectByUsername(String username);	
}

iii)PersistentLoginsManagerImpl實(shí)現(xiàn)類:

package cn.zifangsky.manager.impl;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import cn.zifangsky.manager.PersistentLoginsManager;
import cn.zifangsky.mapper.PersistentLoginsMapper;
import cn.zifangsky.model.PersistentLogins;

@Service("persistentLoginsManagerImpl")
public class PersistentLoginsManagerImpl implements PersistentLoginsManager {
	@Resource(name="persistentLoginsMapper")
	private PersistentLoginsMapper persistentLoginsMapper;

	public int deleteByPrimaryKey(Integer id) {
		return persistentLoginsMapper.deleteByPrimaryKey(id);
	}

	@Override
	public int insert(PersistentLogins pLogins) {
		return persistentLoginsMapper.insert(pLogins);
	}

	@Override
	public int insertSelective(PersistentLogins pLogins) {
		return persistentLoginsMapper.insertSelective(pLogins);
	}

	@Override
	public PersistentLogins selectByPrimaryKey(Integer id) {
		return persistentLoginsMapper.selectByPrimaryKey(id);
	}

	@Override
	public int updateByPrimaryKeySelective(PersistentLogins pLogins) {
		return persistentLoginsMapper.updateByPrimaryKeySelective(pLogins);
	}

	@Override
	public int updateByPrimaryKey(PersistentLogins pLogins) {
		return persistentLoginsMapper.updateByPrimaryKey(pLogins);
	}

	public PersistentLogins selectByUsernameAndSeries(String username, String series) {
		if(StringUtils.isNotBlank(username) && StringUtils.isNotBlank(series))
			return persistentLoginsMapper.selectByUsernameAndSeries(username, series);
		else
			return null;
	}

	@Override
	public PersistentLogins selectByUsername(String username) {
		return persistentLoginsMapper.selectByUsername(username);
	}
}

iv)UserManagerImpl實(shí)現(xiàn)類:

package cn.zifangsky.manager.impl;

import java.util.Calendar;
import java.util.Date;
import java.util.UUID;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import cn.zifangsky.manager.UserManager;
import cn.zifangsky.mapper.UserMapper;
import cn.zifangsky.model.PersistentLogins;
import cn.zifangsky.model.User;
import cn.zifangsky.utils.CookieConstantTable;
import cn.zifangsky.utils.CookieUtils;
import cn.zifangsky.utils.EncryptionUtil;

@Service("userManagerImpl")
public class UserManagerImpl implements UserManager {
	

	@Resource(name = "userMapper")
	private UserMapper userMapper;
	@Resource(name = "persistentLoginsManagerImpl")
	private PersistentLoginsManagerImpl persistentLoginsManagerImpl;

	public int deleteByPrimaryKey(Integer id) {
		return userMapper.deleteByPrimaryKey(id);
	}

	@Override
	public int insert(User user) {
		return userMapper.insert(user);
	}

	@Override
	public int insertSelective(User user) {
		return userMapper.insertSelective(user);
	}

	@Override
	public User selectByPrimaryKey(Integer id) {
		return userMapper.selectByPrimaryKey(id);
	}

	@Override
	public int updateByPrimaryKeySelective(User user) {
		return userMapper.updateByPrimaryKeySelective(user);
	}

	@Override
	public int updateByPrimaryKey(User user) {
		return userMapper.updateByPrimaryKey(user);
	}

	@Override
	public User selectByName(String name) {
		return userMapper.selectByName(name);
	}

	@Override
	public User login(User user, boolean rememberme, HttpServletResponse response) {
		User result = new User();
		// 如果用戶名和密碼不為空,執(zhí)行登錄
		if (StringUtils.isNotBlank(user.getName()) && StringUtils.isNotBlank(user.getPassword())) {
			result = userMapper.selectByUser(user);
			// 如果rememberme為true,則保存cookie值,下次自動登錄
			if (result != null && rememberme == true) {
				// 有效期
				Calendar calendar = Calendar.getInstance();
				calendar.add(Calendar.MONTH, 1); // 一個月
				Date validTime = calendar.getTime();
				// 精確到分的時間字符串
				String timeString = calendar.get(Calendar.YEAR) + "-" + calendar.get(Calendar.MONTH) + "-"
						+ calendar.get(Calendar.DAY_OF_MONTH) + "-" + calendar.get(Calendar.HOUR_OF_DAY) + "-"
						+ calendar.get(Calendar.MINUTE);

				// sha256加密用戶信息
				String userInfoBySha256 = EncryptionUtil
						.sha256Hex(result.getName() + "_" + result.getPassword() + "_" + timeString + "_" + CookieConstantTable.salt);
				// UUID值
				String uuidString = UUID.randomUUID().toString();
				// Cookie值
				String cookieValue = EncryptionUtil.base64Encode(result.getName() + ":" + uuidString);

				// 在數(shù)據(jù)庫中保存自動登錄記錄(如果已有該用戶的記錄則更新記錄)
				PersistentLogins pLogin = persistentLoginsManagerImpl.selectByUsername(result.getName());
				if (pLogin == null) {
					pLogin = new PersistentLogins();
					pLogin.setUsername(result.getName());
					pLogin.setSeries(uuidString);
					pLogin.setToken(userInfoBySha256);
					pLogin.setValidtime(validTime);
					persistentLoginsManagerImpl.insertSelective(pLogin);
				}else{
					pLogin.setSeries(uuidString);
					pLogin.setToken(userInfoBySha256);
					pLogin.setValidtime(validTime);
					persistentLoginsManagerImpl.updateByPrimaryKeySelective(pLogin);
				}

				// 保存cookie
				CookieUtils.addCookie(response, CookieConstantTable.RememberMe, cookieValue, null);
			}

		}
		return result;
	}

	@Override
	public void logout(HttpServletRequest request, HttpServletResponse response) {
		//從session中獲取用戶詳情
		User user = (User) request.getSession().getAttribute("user");
		//刪除數(shù)據(jù)庫中的自動登錄記錄
		PersistentLogins pLogins = persistentLoginsManagerImpl.selectByUsername(user.getName());
		if(pLogins != null)
			persistentLoginsManagerImpl.deleteByPrimaryKey(pLogins.getId());
		//清除session和用于自動登錄的cookie
		request.getSession().removeAttribute("user");
		CookieUtils.delCookie(request, response, CookieConstantTable.RememberMe);
	}
}

注:CookieConstantTable類:

package cn.zifangsky.utils;

public class CookieConstantTable {
	// cookie的有效期默認(rèn)為30天
	public final static int COOKIE_MAX_AGE = 60 * 60 * 24 * 30; 
	//cookie加密時的額外的salt
	public final static String salt = "www.zifangsky.cn";
	//自動登錄的Cookie名
	public final static String RememberMe = "remember-me";
}

CookieUtils類:

package cn.zifangsky.utils;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

public class CookieUtils {

	

	/**
	 * 添加一個新Cookie
	 * 
	 * @author zifangsky
	 * @param response
	 *            HttpServletResponse
	 * @param cookie
	 *            新cookie
	 * 
	 * @return null
	 */
	public static void addCookie(HttpServletResponse response, Cookie cookie) {
		if (cookie != null)
			response.addCookie(cookie);
	}

	/**
	 * 添加一個新Cookie
	 * 
	 * @author zifangsky
	 * @param response
	 *            HttpServletResponse
	 * @param cookieName
	 *            cookie名稱
	 * @param cookieValue
	 *            cookie值
	 * @param domain
	 *            cookie所屬的子域
	 * @param httpOnly
	 *            是否將cookie設(shè)置成HttpOnly
	 * @param maxAge
	 *            設(shè)置cookie的最大生存期
	 * @param path
	 *            設(shè)置cookie路徑
	 * @param secure
	 *            是否只允許HTTPS訪問
	 * 
	 * @return null
	 */
	public static void addCookie(HttpServletResponse response, String cookieName, String cookieValue, String domain,
			boolean httpOnly, int maxAge, String path, boolean secure) {
		if (cookieName != null && !cookieName.equals("")) {
			if (cookieValue == null)
				cookieValue = "";

			Cookie newCookie = new Cookie(cookieName, cookieValue);
			if (domain != null)
				newCookie.setDomain(domain);

			newCookie.setHttpOnly(httpOnly);

			if (maxAge > 0)
				newCookie.setMaxAge(maxAge);

			if (path == null)
				newCookie.setPath("/");
			else
				newCookie.setPath(path);

			newCookie.setSecure(secure);

			addCookie(response, newCookie);
		}
	}

	/**
	 * 添加一個新Cookie
	 * 
	 * @author zifangsky
	 * @param response
	 *            HttpServletResponse
	 * @param cookieName
	 *            cookie名稱
	 * @param cookieValue
	 *            cookie值
	 * @param domain
	 *            cookie所屬的子域
	 * 
	 * @return null
	 */
	public static void addCookie(HttpServletResponse response, String cookieName, String cookieValue, String domain) {
		addCookie(response, cookieName, cookieValue, domain, true, CookieConstantTable.COOKIE_MAX_AGE, "/", false);
	}

	/**
	 * 根據(jù)Cookie名獲取對應(yīng)的Cookie
	 * 
	 * @author zifangsky
	 * @param request
	 *            HttpServletRequest
	 * @param cookieName
	 *            cookie名稱
	 * 
	 * @return 對應(yīng)cookie,如果不存在則返回null
	 */
	public static Cookie getCookie(HttpServletRequest request, String cookieName) {
		Cookie[] cookies = request.getCookies();

		if (cookies == null || cookieName == null || cookieName.equals(""))
			return null;

		for (Cookie c : cookies) {
			if (c.getName().equals(cookieName))
				return (Cookie) c;
		}
		return null;
	}

	/**
	 * 根據(jù)Cookie名獲取對應(yīng)的Cookie值
	 * 
	 * @author zifangsky
	 * @param request
	 *            HttpServletRequest
	 * @param cookieName
	 *            cookie名稱
	 * 
	 * @return 對應(yīng)cookie值,如果不存在則返回null
	 */
	public static String getCookieValue(HttpServletRequest request, String cookieName) {
		Cookie cookie = getCookie(request, cookieName);
		if (cookie == null)
			return null;
		else
			return cookie.getValue();
	}

	/**
	 * 刪除指定Cookie
	 * 
	 * @author zifangsky
	 * @param response
	 *            HttpServletResponse
	 * @param cookie
	 *            待刪除cookie
	 */
	public static void delCookie(HttpServletResponse response, Cookie cookie) {
		if (cookie != null) {
			cookie.setPath("/"); 
			cookie.setMaxAge(0);
			cookie.setValue(null);

			response.addCookie(cookie);
		}
	}

	/**
	 * 根據(jù)cookie名刪除指定的cookie
	 * 
	 * @author zifangsky
	 * @param request
	 *            HttpServletRequest
	 * @param response
	 *            HttpServletResponse
	 * @param cookieName
	 *            待刪除cookie名
	 */
	public static void delCookie(HttpServletRequest request, HttpServletResponse response, String cookieName) {
		Cookie c = getCookie(request, cookieName);
		if (c != null && c.getName().equals(cookieName)) {
			delCookie(response, c);
		}
	}

	/**
	 * 根據(jù)cookie名修改指定的cookie
	 * 
	 * @author zifangsky
	 * @param request
	 *            HttpServletRequest
	 * @param response
	 *            HttpServletResponse
	 * @param cookieName
	 *            cookie名
	 * @param cookieValue
	 *            修改之后的cookie值
	 * @param domain
	 *            修改之后的domain值
	 */
	public static void editCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
			String cookieValue,String domain) {
		Cookie c = getCookie(request, cookieName);
		if (c != null && cookieName != null && !cookieName.equals("") && c.getName().equals(cookieName)) {
			addCookie(response, cookieName, cookieValue, domain);
		}
	}
}

EncryptionUtil類:

package cn.zifangsky.utils;

import java.io.UnsupportedEncodingException;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;

public class EncryptionUtil {
    /**
     * Base64 encode
     * */
    public static String base64Encode(String data){
        return Base64.encodeBase64String(data.getBytes());
    }
     
    /**
     * Base64 decode
     * @throws UnsupportedEncodingException 
     * */
    public static String base64Decode(String data) throws UnsupportedEncodingException{
        return new String(Base64.decodeBase64(data.getBytes()),"utf-8");
    }
     
    /**
     * md5
     * */
    public static String md5Hex(String data){
        return DigestUtils.md5Hex(data);
    }
     
    /**
     * sha1
     * */
    public static String sha1Hex(String data){
        return DigestUtils.sha1Hex(data);
    }
     
    /**
     * sha256
     * */
    public static String sha256Hex(String data){
        return DigestUtils.sha256Hex(data);
    }
     
}

這個方法類本質(zhì)上調(diào)用的是 commons-codec-1.10.jar 這個jar包中的方法

在這個類中,關(guān)于退出登錄就不用多做解釋了,有詳細(xì)注釋自己參考下就行

關(guān)于這個登錄方法,實(shí)際上我這里的執(zhí)行流程是這樣的:

  1. 根據(jù)用戶名、密碼執(zhí)行登錄驗(yàn)證

  2. 如果前臺登錄的form表單中勾選了“30天內(nèi)自動登錄”的選項(xiàng),那么就執(zhí)行下面的保存登錄記錄到persistent_logins這個表以及cookie中;如果沒勾選,那么就直接將驗(yàn)證結(jié)果返回到controller中

  3. 執(zhí)行保存記錄的這個操作,實(shí)際上分為以下兩步操作:a:向表persistent_logins保存記錄,username是當(dāng)前用戶;series是獲取的當(dāng)前的UUID值;token是用戶名、密碼、cookie到期時間、以及自定義的salt經(jīng)過sha256非對稱加密之后的字符串;validTime是到期時間。b:向“remember-me”這個cookie保存的記錄值是用戶名和UUID值經(jīng)過base64編碼之后的字符串

  4. 保存記錄,并返回到controller中操作

(5)Controller層:

package cn.zifangsky.controller;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import cn.zifangsky.manager.UserManager;
import cn.zifangsky.model.User;

@Controller
public class UserController {
	@Resource(name = "userManagerImpl")
	private UserManager userManager;

	/**
	 * 用戶主頁
	 */
	@RequestMapping("/user/index.html")
	public ModelAndView userIndex() {
		return new ModelAndView("user/index");
	}

	/**
	 * 登錄校驗(yàn)
	 */
	@RequestMapping("/check.html")
	public ModelAndView login(@RequestParam("username") String username, @RequestParam("password") String password,
			@RequestParam(name = "remember-me", required = false) boolean rememberme, HttpServletRequest request,
			HttpServletResponse response, RedirectAttributes redirectAttributes) {
		HttpSession session = request.getSession();
		User user = new User();
		user.setName(username);
		user.setPassword(password);

		User result = userManager.login(user, rememberme, response);
		if (result != null) {
			ModelAndView mAndView = null;
			//登錄之前地址
			String callback = (String) session.getAttribute("callback");
			session.removeAttribute("callback"); // 獲取之后移除
			// 基本路徑
			String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
					+ request.getContextPath();
			if (StringUtils.isNotBlank(callback)) {
				String[] urls = callback.split(basePath);
				if (urls.length == 2 && StringUtils.isNotBlank(urls[1])) {
					mAndView = new ModelAndView("redirect:" + urls[1]);
				}else{
					mAndView = new ModelAndView("redirect:/user/index.html");
				}
			}else{
				mAndView = new ModelAndView("redirect:/user/index.html");
			}
			
			session.setAttribute("user", result); // 登錄成功之后加入session中
			redirectAttributes.addFlashAttribute("user", result); 

			return mAndView;
		} else {
			return new ModelAndView("redirect:/login.html");
		}
	}

	/**
	 * 退出登錄
	 */
	@RequestMapping("/logout.html")
	public ModelAndView logout(HttpServletRequest request, HttpServletResponse response) {
		ModelAndView mAndView = new ModelAndView("redirect:/login.html");

		userManager.logout(request, response);

		return mAndView;
	}
}

在這里,對“callback”的操作主要是在攔截器中判斷是否能夠自動登錄時,如果能夠登錄那么不用多說直接轉(zhuǎn)到目標(biāo)頁面;如果不能通過驗(yàn)證,那么需要跳轉(zhuǎn)到登錄頁面進(jìn)行用戶名、密碼登錄,這里的callback參數(shù)的目的就是在攔截器中驗(yàn)證失敗跳轉(zhuǎn)到登錄頁面之前,將本來想要訪問的頁面路徑存儲在session中,然后在controller中登錄成功之后從session中取出,最后再重定向到那個目標(biāo)頁面

如果對這里的重定向等代碼不太理解的話,建議可以參考下我在本篇文章開始時列舉的那幾篇文章

(6)幾個測試使用的前臺頁面:

首先給出這幾個頁面之間的層次關(guān)系:

SpringMVC中使用Interceptor+Cookie實(shí)現(xiàn)在一定天數(shù)之內(nèi)自動登錄

i)login.jsp:

<%@page import="java.security.SecureRandom"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>    



">
SpringMVC Cookie Demo
<%
	SecureRandom random = new SecureRandom();
	random.setSeed(8738);
	double _csrf = random.nextDouble();
	session.setAttribute("_csrf", _csrf);
%>


	
		

SpringMVC Cookie Demo

用戶名:
密碼:
30天內(nèi)自動登錄
" />

登錄用的form表單

ii)user目錄下的index.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>    



">
SpringMVC Cookie Demo


	
		

SpringMVC Cookie Demo

退出登錄
Hello ${user.name},welcome to user home page!

iii)callback.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>




	">
	SpringMVC Cookie Demo


	
		

SpringMVC Cookie Demo

測試 callback 頁面跳轉(zhuǎn)

這個頁面主要是為了測試登錄之后是否能夠跳轉(zhuǎn)到原來想要訪問的頁面

(7)攔截器UserInterceptor:

package cn.zifangsky.interceptor;

import java.util.Calendar;
import java.util.Date;
import java.util.UUID;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import cn.zifangsky.manager.UserManager;
import cn.zifangsky.manager.impl.PersistentLoginsManagerImpl;
import cn.zifangsky.model.PersistentLogins;
import cn.zifangsky.model.User;
import cn.zifangsky.utils.CookieConstantTable;
import cn.zifangsky.utils.CookieUtils;
import cn.zifangsky.utils.EncryptionUtil;

public class UserInterceptor extends HandlerInterceptorAdapter {
	@Resource(name = "persistentLoginsManagerImpl")
	private PersistentLoginsManagerImpl persistentLoginsManagerImpl;
	@Resource(name = "userManagerImpl")
	private UserManager userManager;

	/**
	 * 用于處理自動登錄
	 */
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		HttpSession session = request.getSession();
		User user = (User) session.getAttribute("user");

		// 已登錄
		if (user != null) {
			return true;
		} else {
			// 從cookie中取值
			Cookie rememberme = CookieUtils.getCookie(request, CookieConstantTable.RememberMe);
			if (rememberme != null) {
				String cookieValue = EncryptionUtil.base64Decode(rememberme.getValue());

				String[] cValues = cookieValue.split(":");
				if (cValues.length == 2) {
					String usernameByCookie = cValues[0]; // 獲取用戶名
					String uuidByCookie = cValues[1]; // 獲取UUID值
					// 到數(shù)據(jù)庫中查詢自動登錄記錄
					PersistentLogins pLogins = persistentLoginsManagerImpl.selectByUsernameAndSeries(usernameByCookie,
							uuidByCookie);
					if (pLogins != null) {
						String savedToken = pLogins.getToken(); // 數(shù)據(jù)庫中保存的密文

						// 獲取有效時間
						Date savedValidtime = pLogins.getValidtime();
						Date currentTime = new Date();

						// 如果還在cookie有效期之內(nèi),繼續(xù)判斷是否可以自動登錄
						if (currentTime.before(savedValidtime)) {
							User u = userManager.selectByName(usernameByCookie);
							if (u != null) {
								Calendar calendar = Calendar.getInstance();
								calendar.setTime(pLogins.getValidtime());
								// 精確到分的時間字符串
								String timeString = calendar.get(Calendar.YEAR) + "-" + calendar.get(Calendar.MONTH)
										+ "-" + calendar.get(Calendar.DAY_OF_MONTH) + "-"
										+ calendar.get(Calendar.HOUR_OF_DAY) + "-" + calendar.get(Calendar.MINUTE);
								// 為了校驗(yàn)而生成的密文
								String newToken = EncryptionUtil.sha256Hex(u.getName() + "_" + u.getPassword() + "_"
										+ timeString + "_" + CookieConstantTable.salt);

								// 校驗(yàn)sha256加密的值,如果不一樣則表示用戶部分信息已被修改,需要重新登錄
								if (savedToken.equals(newToken)) {
									/**
									 * 為了提高安全性,每次登錄之后都更新自動登錄的cookie值
									 */
									// 更新cookie值
									String uuidNewString = UUID.randomUUID().toString();
									String newCookieValue = EncryptionUtil
											.base64Encode(u.getName() + ":" + uuidNewString);
									CookieUtils.editCookie(request, response, CookieConstantTable.RememberMe,
											newCookieValue, null);

									// 更新數(shù)據(jù)庫
									pLogins.setSeries(uuidNewString);
									persistentLoginsManagerImpl.updateByPrimaryKeySelective(pLogins);

									/**
									 * 將用戶加到session中,不退出瀏覽器時就只需判斷session即可
									 */
									session.setAttribute("user", u);

									return true;  //校驗(yàn)成功,此次攔截操作完成
								} else { // 用戶部分信息被修改,刪除cookie并清空數(shù)據(jù)庫中的記錄
									CookieUtils.delCookie(response, rememberme);
									persistentLoginsManagerImpl.deleteByPrimaryKey(pLogins.getId());
								}
							}
						} else { // 超過保存的有效期,刪除cookie并清空數(shù)據(jù)庫中的記錄
							CookieUtils.delCookie(response, rememberme);
							persistentLoginsManagerImpl.deleteByPrimaryKey(pLogins.getId());
						}
					}
				}
			}
			//將來源地址存放在session中,登錄成功之后跳回原地址
			String callback = request.getRequestURL().toString();
			session.setAttribute("callback", callback);
			response.sendRedirect(
					request.getContextPath() + "/login.html?callback=" + callback);
			return false;
		}
	}

	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {

		super.afterCompletion(request, response, handler, ex);
	}

}

在這里,驗(yàn)證流程如下:

  1. 如果在session中存在“user”對象,那么驗(yàn)證通過,允許訪問

  2. 如果session中沒有,則需要取出cookie中名字為“remember-me”對應(yīng)的值,用于下一步驗(yàn)證

  3. 根據(jù)base64解碼之后的用戶名和UUID值從表“persistent_logins”查詢記錄

  4. 取出數(shù)據(jù)庫中保存的token值和到期時間,根據(jù)同樣的加密方法加密待校驗(yàn)的密文,然后和數(shù)據(jù)庫中的token相比較

  5. 如果一樣,則表示可以自動登錄。同時,為了提高安全性,在校驗(yàn)成功之后更新用戶端的用于自動登錄的cookie記錄

  6. 將“user”對象添加到session中,本次攔截器校驗(yàn)通過

當(dāng)然,我這里只是簡單敘述了下流程,更具體的流程可以自行參考代碼中的注釋

三 測試

(1)測試使用cookie實(shí)現(xiàn)自動登錄:

啟動項(xiàng)目后,訪問:http://localhost:9180/CookieDemo/login.html

輸入用戶名、密碼并勾上“30天內(nèi)自動登錄”:

SpringMVC中使用Interceptor+Cookie實(shí)現(xiàn)在一定天數(shù)之內(nèi)自動登錄

點(diǎn)擊登錄之后,可以發(fā)現(xiàn)頁面跳轉(zhuǎn)到了:http://localhost:9180/CookieDemo/user/index.html

同時生成了一條名為“remember-me”的cookie記錄值,其值是:YWRtaW46YzhjYTU3NjktNDhjZi00NWQ4LTk4YzQtM2QzMDMwNWVlMWY5

SpringMVC中使用Interceptor+Cookie實(shí)現(xiàn)在一定天數(shù)之內(nèi)自動登錄如果使用在線base64解碼工具解碼之后可以發(fā)現(xiàn),這個cookie值的原文是:

SpringMVC中使用Interceptor+Cookie實(shí)現(xiàn)在一定天數(shù)之內(nèi)自動登錄

恰好與數(shù)據(jù)庫中persistent_logins表中的記錄相對應(yīng):

SpringMVC中使用Interceptor+Cookie實(shí)現(xiàn)在一定天數(shù)之內(nèi)自動登錄接著,退出瀏覽器之后再次打開該瀏覽器訪問:http://localhost:9180/CookieDemo/user/index.html

可以發(fā)現(xiàn):可以直接訪問該頁面,同時已經(jīng)是登錄狀態(tài)了。到此,我們的目的已經(jīng)達(dá)成了

(2)測試登錄之后跳回到原來想要訪問的頁面:

刪除瀏覽器中的“remember-me”這個cookie,或者刪掉數(shù)據(jù)庫中persistent_logins表中的記錄,然后在退出登錄之后訪問:http://localhost:9180/CookieDemo/user/callback.html

可以發(fā)現(xiàn),頁面已經(jīng)被自動重定向到登錄頁面了

SpringMVC中使用Interceptor+Cookie實(shí)現(xiàn)在一定天數(shù)之內(nèi)自動登錄

接著,輸入用戶名、密碼登錄,可以發(fā)現(xiàn):在登錄成功之后能夠正常跳轉(zhuǎn)到我們原來請求的頁面:

SpringMVC中使用Interceptor+Cookie實(shí)現(xiàn)在一定天數(shù)之內(nèi)自動登錄附:本次測試項(xiàng)目的完整源代碼:

PS:上面圖片中的水印是我個人博客的域名,因此還請管理員手下留情不要給我標(biāo)為“轉(zhuǎn)載文章”,謝謝!!!


網(wǎng)站欄目:SpringMVC中使用Interceptor+Cookie實(shí)現(xiàn)在一定天數(shù)之內(nèi)自動登錄
網(wǎng)站URL:http://m.jcarcd.cn/article/pdopoe.html

其他資訊

主站蜘蛛池模板: 日本中文字幕视频久 | 午夜日韩高清 | 国产成自拍亚洲精品 | 三年片免费观看大全 | 国偷精品免费看 | 日韩一级在线播放 | 国产探花在线播放 | 日韩国产在线va | 国产区精品自拍 | 国产日韩a | 日韩欧美一区 | 精品免费视频大 | 日本美女一级视频 | 国产丝袜美女一区 | 日韩亚色五月 | 欧美日韩一二三区 | 欧美在线一级精品 | 国产激情网 | 国产精品在线观看 | 91视频免费看 | 亲子乱子伦xxxx | 午夜国产一区 | 国产男女猛烈无 | 国产合集在线播放 | 国产日韩精品aⅴ | 国产萝控精 | 国产无人区 | 国产精品地址 | 午夜国产在线观看 | 国产激情在线观看 | 午夜成年人视频体验 | 91短视| 国产在线日韩欧美 | 国产一区二区免费 | 国产韩日欧美在线 | 国内精自线i | 91神马电 | 国产一区二区三四区 | 国产韩日欧美在线 | 国产美女精品在线 | 欧洲视频中文字幕在 |