您好,我是湘王,這是我的部落格園,歡迎您來,歡迎您再來~
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,原理都是一樣的。
感謝您的大駕光臨!諮詢技術、產品、運營和管理相關問題,請關注後留言。歡迎騷擾,不勝榮幸~