您好,我是湘王,這是我的博客園,歡迎您來,歡迎您再來~ Spring Security使用MySQL保存cookie記錄雖然方便,但是目前更多的主流互聯網應用都是用NoSQL來保存非業務數據的,Spring Security也應該可以實現這個功能。之前Spring Security官方並不支持使用N ...
您好,我是湘王,這是我的博客園,歡迎您來,歡迎您再來~
Spring Security使用MySQL保存cookie記錄雖然方便,但是目前更多的主流互聯網應用都是用NoSQL來保存非業務數據的,Spring Security也應該可以實現這個功能。之前Spring Security官方並不支持使用NoSQL來保存cookie,但這個問題對於一個愛鑽研的碼農來說應該只是個小CASE——畢竟只要有代碼,就沒有搞不定的問題——受JdbcTokenRepositoryImpl的啟發,查看其源碼,可以發現JdbcDaoSupport只是用來提供數據源,無實際意義,而PersistentTokenRepository才是要實現的介面。
JdbcTokenRepositoryImpl的源碼非常簡單,看懂了就能照著寫出Mongo的實現。題外話:閱讀源碼是個能夠很快提高開發能力的捷徑,如Spring框架、Spark源碼等等。
下麵就來開始咱們的NoSQL改造DIY。
首先安裝並運行mongodb(我用的是mongodb-4.2.6),可以是虛擬機,也可以是Docker。
再修改項目的pom.xml文件,增加mongodb依賴:
通過模仿JdbcDaoSupport,來自定義自己的MongoDaoSupport:
/**
* MongoDaoSupport
*
* @author 湘王
*/
@Component
public class MongoDaoSupport<T> {
@Autowired
private MongoTemplate mongoTemplate;
// 插入數據
public boolean insert(PersistentRememberMeToken token) {
if (Objects.isNull(token)) {
return false;
}
Object object = mongoTemplate.save(token);
if (Objects.nonNull(object)) {
return true;
}
return false;
}
}
然後再在MongoDaoSupport中增加兩個重要的方法,讓它支持Mongodb:
// 查詢數據
public PersistentRememberMeToken getTokenBySeries(String series) {
// // 如果是通過字元串方式諸葛插入欄位值,那麼通過mongoTemplate.findOne()得到的就是一個LinkedHashMap
// LinkedHashMap<String, String> map = (LinkedHashMap<String, String>) mongoTemplate
// .findOne(query, Object.class, "collectionName");
// return new PersistentRememberMeToken(map.get("username"), map.get("series"),
// map.get("tokenValue"), DateUtils.format()map.get("date"));
Query query = new Query(Criteria.where("series").is(series));
// // 這裡原路返回PersistentRememberMeToken對象,不會是LinkedHashMap
// Object object = mongoTemplate.findOne(query, PersistentRememberMeToken.class);
// return (PersistentRememberMeToken) obejct;
return mongoTemplate.findOne(query, PersistentRememberMeToken.class);
}
// 更新數據
public boolean updateToken(String series, String tokenValue, Date lastUsed) {
Query query = new Query(Criteria.where("series").is(series));
Update update = new Update();
update.set("tokenValue", tokenValue);
update.set("date", lastUsed);
// // 這裡不能用DateUtils.parse(new Date()),否則getTokenBySeries()方法會拋出非法參數異常
// update.set("date", DateUtils.parse(new Date()));
Object object = mongoTemplate.updateMulti(query, update, PersistentRememberMeToken.class);
if (Objects.nonNull(object)) {
return true;
}
return false;
}
然後再定義MongoTokenRepositoryImpl:
/**
* 自定義實現token持久化到mongodb
*
* @author 湘王
*/
public class MongoTokenRepositoryImpl implements PersistentTokenRepository {
@Autowired
private MongoDaoSupport<PersistentRememberMeToken> mongoDaoSupport;
@Override
public void createNewToken(PersistentRememberMeToken token) {
mongoDaoSupport.insert(token);
}
@Override
public void updateToken(String series, String tokenValue, Date lastUsed) {
mongoDaoSupport.updateToken(series, tokenValue, lastUsed);
}
@Override
public PersistentRememberMeToken getTokenForSeries(String series) {
return mongoDaoSupport.getTokenBySeries(series);
}
@Override
public void removeUserTokens(String username) {
}
}
接著在WebSecurityConfiguration中用自定義的MongoTokenRepositoryImpl代替JdbcTokenRepositoryImpl:
// NoSQL方式實現記住我
@Bean
public PersistentTokenRepository persistentTokenRepository() {
// 自定義mongo方式實現
MongoTokenRepositoryImpl mongoTokenRepository = new MongoTokenRepositoryImpl();
return mongoTokenRepository;
}
或者就直接(需要通過@Service註解註入):
運行postman測試,可以看到通過MongoDB實現了對cookie信息的存儲與修改。
如果mongodb中多出了_class欄位,可以加上額外的配置:
/**
* 去除_class欄位
*
* @author 湘王
*/
@Configuration
public class MongoConfiguration implements InitializingBean {
@Autowired
@Lazy
private MappingMongoConverter mappingMongoConverter;
@Override
public void afterPropertiesSet() {
mappingMongoConverter
.setTypeMapper(new DefaultMongoTypeMapper(null));
}
}
多次運行可發現訪問記錄值的規律:
1、同一用戶會有多條訪問記錄
如果每次都明確執行login方法,那麼每次都會產生不同的記錄,否則只會更新同一條記錄的tokenValue和date值;
若token有效且未執行login方法,那麼將更新最後一次產生的記錄的tokenValue和date值。
2、這說明token條數是與login方法執行次數一一對應的;
3、只要token不失效,僅更新同一條記錄series的token值。
訪問數據記錄:
不管是Mongodb還是別的NoSQL,比如Redis,原理都是一樣的。
感謝您的大駕光臨!咨詢技術、產品、運營和管理相關問題,請關註後留言。歡迎騷擾,不勝榮幸~