从零开始java安全权限框架篇(三):记住我Token存储的源码解析以及自定义Token存储

目录

一:记住我功能的Token持久化

1.新建数据表结构

2.使用JdbcTemplate来存储生成的Token

二:记住我功能的源码解析

1.首先来看一下PersistentTokenRepository接口

2.PersistentTokenRepository的实现

3.1PersistentTokenRepository实现之一InMemoryTokenRepositoryImpl

 (1)定义一个内存存储的Map

          (2)定义一个根据主键查询Tokne的同步方法

(3)创建Token

(4)更新Token

 (5)删除Token

3.2PersistentTokenRepository实现之二JdbcTokenRepositoryImpl

三:自定义实现记住我功能的持久化

1.为什么要自定义持久化

2.如何持久化

3.实现自定义持久化

(1)定义好RedisConfig并且完成工具类

(2)自定义MyPersistentRememberMeToken

(3)自定义myPersistentTokenRepository实现PersistentTokenRepository接口



一:记住我功能的Token持久化

  在(一)中,我们将“记住我功能”放入数据库,进行数据持久化。主要步骤如下,我们看一下具体的:

1.新建数据表结构

从零开始java安全权限框架篇(三):记住我Token存储的源码解析以及自定义Token存储_第1张图片

2.使用JdbcTemplate来存储生成的Token

从零开始java安全权限框架篇(三):记住我Token存储的源码解析以及自定义Token存储_第2张图片

二:记住我功能的源码解析

1.首先来看一下PersistentTokenRepository接口

从零开始java安全权限框架篇(三):记住我Token存储的源码解析以及自定义Token存储_第3张图片

这个接口定义的功能很明了,Token的Crud

2.PersistentTokenRepository的实现

 这个接口有2个实现类,如图所示:

       

3.1PersistentTokenRepository实现之一InMemoryTokenRepositoryImpl

   这个是Token的默认的存储方式,使用了内存的方式存储。我们来看一下结构:

 (1)定义一个内存存储的Map

 (2)定义一个根据主键查询Tokne的同步方法

     说明一下,这里为什么要在方法加锁呢?保证同时只能有一个线程读取。

public synchronized PersistentRememberMeToken getTokenForSeries(String seriesId) {
		return seriesTokens.get(seriesId);
	}

(3)创建Token

public synchronized void createNewToken(PersistentRememberMeToken token) {
		PersistentRememberMeToken current = seriesTokens.get(token.getSeries());

		if (current != null) {
			throw new DataIntegrityViolationException("Series Id '" + token.getSeries()
					+ "' already exists!");
		}

		seriesTokens.put(token.getSeries(), token);
	}

  来看一下这个PersistenToken中有哪些属性:

从零开始java安全权限框架篇(三):记住我Token存储的源码解析以及自定义Token存储_第4张图片

 

(4)更新Token

public synchronized void updateToken(String series, String tokenValue, Date lastUsed) {
		PersistentRememberMeToken token = getTokenForSeries(series);

		PersistentRememberMeToken newToken = new PersistentRememberMeToken(
				token.getUsername(), series, tokenValue, new Date());

		// Store it, overwriting the existing one.
		seriesTokens.put(series, newToken);
	}

 (5)删除Token

 代码还是很简单的啊

public synchronized void removeUserTokens(String username) {
		Iterator series = seriesTokens.keySet().iterator();

		while (series.hasNext()) {
			String seriesId = series.next();

			PersistentRememberMeToken token = seriesTokens.get(seriesId);

			if (username.equals(token.getUsername())) {
				series.remove();
			}
		}
	}

3.2PersistentTokenRepository实现之二JdbcTokenRepositoryImpl

 其实这个实现对于上一步5个同步的方法还是一样的思路,主要区别就是这个实现基于数据库实现了

 以及CRUD的sql:

从零开始java安全权限框架篇(三):记住我Token存储的源码解析以及自定义Token存储_第5张图片

三:自定义实现记住我功能的持久化

1.为什么要自定义持久化

  首先来说明一下,为甚要实现自定义的持久化。我们基于上面的2中存储方法来分析:

  第一种:内存Map的存储

    (1)这种方法只支持本地存储,一旦集群或者分布就蒙了

    (2)使用内存,一旦用户量大了,内存极有可能就GG了

   第二种:数据库的存储

     (1)从数据库存取,太占用资源,包括但不限于内存,线程等

     (2)响应时间太久,毕竟是从数据库存取,一旦数据量大,就会导致读取速度变慢

2.如何持久化

  综合上面的,我们需要考虑到2点:需要支持集群以及合理的GC策略。

 本节我们选用Ehcache来做持久化,主要当前只选用了这个缓存框架,实际项目中都是使用Redis的。

 因为使用了缓存,实则同Map存储方式是一致的。我们参照第一种存储方式来写

3.实现自定义持久化

(1)定义好RedisConfig并且完成工具类

       详情见:https://pan.baidu.com/s/1lKRqgeRcueJoIQ9gEwUSWA           

(2)自定义MyPersistentRememberMeToken

 定义好MyPersistentRememberMeToken,结构同PersistentRememberMeToken,主要是为了处理PersistentRememberMeToken这个实体类无法用redisTelepate无法反序列化的问题。



package com.config.Seurity.model;

import java.io.Serializable;
import java.util.Date;

import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
 *
 * ClassName:實現PersistentRememberMeToken反序列化 
* Reason: TODO ADD REASON.
* Date: 2019年9月3日 上午11:25:52
* @author Owner * @version * @since JDK 1.8 * @see */ @Setter @Getter @AllArgsConstructor @NoArgsConstructor public class MyPersistentRememberMeToken implements Serializable{ /** * serialVersionUID:TODO(用一句话描述这个变量表示什么). * @since JDK 1.8 */ private static final long serialVersionUID = 1L; private String username; private String series; private String tokenValue; private Date date; public PersistentRememberMeToken myToEntity(MyPersistentRememberMeToken myPersistentRememberMeToken) { if(myPersistentRememberMeToken == null ) { return null; } return new PersistentRememberMeToken( myPersistentRememberMeToken.getUsername(), myPersistentRememberMeToken.getSeries(), myPersistentRememberMeToken.getTokenValue(), myPersistentRememberMeToken.getDate()); } public MyPersistentRememberMeToken(PersistentRememberMeToken persistentRememberMeToken) { this.username=persistentRememberMeToken.getUsername(); this.series=persistentRememberMeToken.getSeries(); this.tokenValue=persistentRememberMeToken.getTokenValue(); this.date=persistentRememberMeToken.getDate(); } }

(3)自定义myPersistentTokenRepository实现PersistentTokenRepository接口



package com.config.Seurity.repository;

import java.util.Date;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import com.config.Seurity.model.MyPersistentRememberMeToken;
import com.config.redis.redisService;

import commons.json.JSONObject;





/**
 *
 * 双token存取方案说明:
 *  (1)在InMemoryTokenRepositoryImpl中,实际上是以series为存储的Key,
 *  (2)在Remove()中,遍历所有的Map,然后找出userName的Token,在更具找出Token的series来删除的
 *  (3)上述方法遍历所有的Map,代价太大了。
 * 双Token实现说明
 * (1)以series的存储为主同步方法
 * (2)以Username的存取的次异步方法
 *
 *
 * ClassName:自定义Token存储 
* Reason: 注意,这里所有的方法都用了锁,实际上入股哦使用redis做存储,是不需要的.
* Date: 2019年9月2日 上午10:56:30
* @author Owner * @version * @since JDK 1.8 * @see */ @Component public class myPersistentTokenRepository implements PersistentTokenRepository{ @Resource private redisService redisService; //series作为Key的前缀 private static final String seriesKeyPre="series"; //userName作为Key的前缀 private static final String userNameKeyPre="userName"; private static final Long timeout=30L; //=====工具方法的封装 /** * 主同步Token的存储 * @param key * @param t * @author Owner * @time:2019年9月2日下午12:13:18 * @since JDK 1.8 */ private synchronized void putCache(PersistentRememberMeToken token) { MyPersistentRememberMeToken myToken=new MyPersistentRememberMeToken(token); redisService.set(seriesKeyPre+myToken.getSeries() ,myToken ,timeout); } /** * 次异步Token的存储 * @param token * @author Owner * @time:2019年9月2日下午12:29:43 * @since JDK 1.8 */ @Async private void putUserToken(PersistentRememberMeToken token) { MyPersistentRememberMeToken myToken=new MyPersistentRememberMeToken(token); redisService.set(userNameKeyPre+myToken.getUsername() ,myToken,timeout ); } /** * 从缓存中获取userName * @param userName * @return * @author Owner * @time:2019年9月2日下午12:51:23 * @since JDK 1.8 */ public MyPersistentRememberMeToken getTokenForUserName(String userName) { return redisService.get(userNameKeyPre+userName); } public void removeTokenByUserName(String userName) { MyPersistentRememberMeToken myToken=redisService.get(userNameKeyPre+userName); redisService.remove(userNameKeyPre+userName); redisService.remove(seriesKeyPre+myToken.getSeries()); } //主要实现的方法 /** * * TODO:从缓存中获取series * @see org.springframework.security.web.authentication.rememberme.PersistentTokenRepository#getTokenForSeries(java.lang.String) */ @Override public synchronized PersistentRememberMeToken getTokenForSeries(String seriesId) { MyPersistentRememberMeToken myToken= redisService.get(seriesKeyPre+seriesId); return myToken == null ? null :myToken.myToEntity(myToken); } /** * * TODO 存储Token. * @see org.springframework.security.web.authentication.rememberme.PersistentTokenRepository#createNewToken(org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken) */ @Override public synchronized void createNewToken(PersistentRememberMeToken token) { PersistentRememberMeToken current = (PersistentRememberMeToken) getTokenForSeries(token.getSeries()); if (current != null) { throw new DataIntegrityViolationException("Series Id '" + token.getSeries() + "' already exists!"); } putCache(token); putUserToken(token); } @Override public void updateToken(String series, String tokenValue, Date lastUsed) { PersistentRememberMeToken token = getTokenForSeries(series); PersistentRememberMeToken newToken = new PersistentRememberMeToken( token.getUsername(), series, tokenValue, new Date()); putCache(newToken); putUserToken(token); } @Override public void removeUserTokens(String username) { removeTokenByUserName(username); } }

                

 

你可能感兴趣的:(从零开始,security,Remeber)