基於token的登入管理(多裝置登入、單裝置登入)
阿新 • • 發佈:2018-11-19
不管是客戶端介面還是網頁H5介面,一般我們都需要登入驗證,即要求所有的介面訪問都必須在登入之後,以確認身份,防止非法呼叫。一般的流程都是登入的時候返回一個代表此登入的token,以後所有介面都帶上此token,在所有介面呼叫之前攔截驗證,一般都是通過AOP或者一個Filter、攔截器來實現。而退出的時候呼叫介面將此token刪除即可。一般地,為了對介面侵入最小,能做到統一處理,可以將此token放在header中。token一般都會設定一個有效期,過期了直接提示呼叫者需要登入以控制條轉到登入頁面引導登入。
服務端設計:
/** * token管理器 * @author xiongshiyan */ public interface TokenManager<M> { /** * 生成token * @param m 實體 * @return token值 */ String createToken(M m); String createToken(M m , long expires); /** * 根據token獲取 * @param token token * @return 根據token獲取的實體 */ M findByToken(String token); /** * 更新token的過期 * @param token token */ void updateExpires(String token); void updateExpires(String token , long expires); /** * 刪除 * @param token token * @return 刪除是否成功 */ boolean deleteToken(String token); /** * 產生token * @param m 實體 * @return token */ String getToken(M m); }
M代表登入實體,也可以是能代表登入人的唯一標識,使用泛型指定。createToken用於生成並儲存token,findByToken用於通過token找到登入實體,updateExpires用於更新token的過期時間,deleteToken用於刪除token(登入退出的時候),getToken生成token字串。一般驗證token是幾乎每個介面都會用,所以必須保證速度,可以採用redis來儲存。
/** * 基於redis的token管理器基類 * @author xiongshiyan at 2018/8/15 , contact me with email [email protected]
or phone 15208384257 */ public abstract class AbstractRedisTokenManager<M> implements TokenManager<M> { protected RedisUtil redisUtil; public AbstractRedisTokenManager(RedisUtil redisUtil){ this.redisUtil = redisUtil; } @Override public String createToken(M m, long expires) { String token = getToken(m); redisUtil.set(token , m , expires); return token; } @Override public M findByToken(String token) { if(null == token){ return null; } Object o = redisUtil.get(token); if(null == o){ return null; } return (M) o; } @Override public void updateExpires(String token , long expires){ if(null == token){ return; } redisUtil.expire(token , expires); } @Override public boolean deleteToken(String token){ Object o = redisUtil.get(token); if(null == o){ return false; } redisUtil.del(token); return true; } }
此基類實現了一些公共的方法,繼承此類實現剩餘的方法即可。
1.允許多裝置登入的實現。這種情形下,token可以隨意生成,只要保證不重複即可。
/**
* 客戶端API介面token管理器【支援多裝置登入】
* @author xiongshiyan at 2018/8/15 , contact me with email [email protected] or phone 15208384257
*/
public class ApiTokenManager extends AbstractRedisTokenManager<Member> {
private String apiTokenPrefix;
private long apiExpires;
public ApiTokenManager(RedisUtil redisUtil, String apiTokenPrefix, long apiExpires) {
super(redisUtil);
this.apiTokenPrefix = apiTokenPrefix;
this.apiExpires = apiExpires;
}
@Override
public String createToken(Member m) {
return createToken(m , apiExpires);
}
@Override
public void updateExpires(String token){
updateExpires(token , apiExpires);
}
@Override
public String getToken(Member m){
return apiTokenPrefix + "-" + nowStr() + CommonUtil.randomString(16);
}
private String nowStr(){
return DatetimeUtils.toStr(new Date() , DatetimeUtils.SDF_DATETIME_SHORT);
}
}
Member就代表登入實體。
2.只允許單裝置登入,即所謂的登入踢人,在登入的時候驗證是夠已經登入,如果已經登入就給出提示或者直接踢人登入。這種情形下,需要根據登入的標識確認是否已經登入,有兩種解決方式,一種是在儲存token的時候,既儲存token》》實體的關係,還需要儲存實體標識與token的關係;另外一種解決方式是token與實體標識強相關,根據實體標識即可算出token。第二種的實現為:
/**
* 客戶端API介面token管理器【只支援單裝置登入】
* @author xiongshiyan at 2018/8/15 , contact me with email [email protected] or phone 15208384257
*/
public class ApiTokenManager extends AbstractRedisTokenManager<Member> {
private long apiExpires;
public ApiTokenManager(RedisUtil redisUtil , long apiExpires){
super(redisUtil);
this.apiExpires = apiExpires;
}
@Override
public String createToken(Member m) {
return createToken(m , apiExpires);
}
@Override
public void updateExpires(String token){
updateExpires(token , apiExpires);
}
/**
* 單裝置的token必須跟賬號強相關,即根據賬號要能算出token
* @param m m
*/
@Override
public String getToken(Member m){
return m.getPhone();
}
}