《App接口之Token令牌實(shí)現(xiàn)》
轉(zhuǎn)載請注明來自 傻小孩b_移動開發(fā)(http://www.itdecent.cn/users/d388bcf9c4d3)喜歡的可以關(guān)注我,不定期總結(jié)文章!您的支持是我的動力哈!
1、目的
眾所周知,在web端中,Token(令牌)只是作為防止用戶重復(fù)提交表單的作用而存在。但是對于App客戶端而言,Token卻充當(dāng)著另一種角色,類似現(xiàn)實(shí)生活中代表每個人的角色認(rèn)證、或者類似瀏覽器cookie代表你訪問網(wǎng)站的角色認(rèn)證。前提,在有用戶系統(tǒng)的應(yīng)用中,在每次訪問接口的時候,為了避免接口裸露被被無止境的請求攻擊,往往我們會利用一種機(jī)制,過濾一切非應(yīng)用用戶端的非合法請求。首先我們不可能每次利用賬號密碼作為我們的過濾標(biāo)準(zhǔn)(會存在被抓包密碼泄露風(fēng)險(xiǎn)),因此便有
Token(令牌)的存在。即在存在這里的Token是指在指定有效時間內(nèi)可以代表用戶角色,具有請求接口的權(quán)限。
當(dāng)然,這里有開發(fā)者會提問,為什么不適用session。理論上是可以的,只是如果是有接觸過這部分的移動開發(fā)者,session本地是不好處理的,并且完全依賴
session,會被黑客截取后模擬請求,依然會存在被攻擊的風(fēng)險(xiǎn)。
2、實(shí)現(xiàn)思路
這里我不做加密方式選擇的舉例,這里只是做了簡單的做了不可逆的MD5加密方式(賬號+時間戳)。首先web框架是利用(spring + struct2 + mybatis),簡單說明下實(shí)現(xiàn)思路:
1、用戶登錄。請求登錄接口,如果賬號密碼核對正確,會根據(jù)賬號和時間戳進(jìn)行Md5加密生成Token
2、服務(wù)端雙向保存token。服務(wù)端根據(jù)有效時間內(nèi)生成對應(yīng)的token之后,服務(wù)端雙向保存了Memcache中(
Memcache 是一種分布式緩存存儲機(jī)制,這里我就不詳細(xì)說明了),最后通過登錄接口返回Token信息至客戶端中
3、客戶端保存返回Token。
客戶端通過登錄接口成功返回的token,保存的內(nèi)存中,在每次請求接口都要攜帶這個token,進(jìn)行接口請求。
具體思路圖如下所示:


3、案例
(1)登錄接口實(shí)現(xiàn)
/**
* 用戶登錄
* @throws IOException
*/
public void login() throws IOException{
System.out.println("- AppUserManagerAction -" + "login");
String token = TokenUtils.setToken(testAccount);
System.out.println("賬號 " + testAccount + "生成的token:"+token);
JSONObject json = new JSONObject();
json.put("result", "0");
json.put("reason", "用戶");
json.put("token", token);
this.getResponse().setContentType("text/plain");
this.getResponse().setCharacterEncoding("UTF-8");
this.getResponse().getWriter().write(json.toString());
this.getResponse().getWriter().flush();
}
(2)token生成
/**
* 為了登陸 或 刷新 Token 生成對應(yīng)token
*/
public static String setToken(String account){
String token = generateToken(account);
// 存儲正反向
MemcacheManager.set(account + TOKEN_MARKER, token,TOKEN_VAILD_TIME);
MemcacheManager.set(token,account + TOKEN_MARKER,TOKEN_VAILD_TIME);
return token;
}
(3)struct2 請求攔截
首先在接口請求中,為了對token進(jìn)行驗(yàn)證,這里是直接在
struct2中寫了一個攔截器對請求接口用戶Token的驗(yàn)證,有疑問的可以自己谷歌搜索下struct2自定義攔截器
/**
* Token 攔截器 用于檢測Token是否過去,過期直接不執(zhí)行Action
* @author wsy
*
*/
public class TokenInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("-- TokenInterceptor --");
Object action = invocation.getAction();
if (action instanceof MngUserAction || action instanceof AppUserManagerAction) {
if (invocation.getProxy().getActionName().equals("login")) {
System.out.println("-- TokenInterceptor -- " + "login 不需要攔截");
return invocation.invoke();
}
}
// 取得請求相關(guān)的ActionContext實(shí)例
ActionContext actionContext = invocation.getInvocationContext();
HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);
HttpServletResponse response = (HttpServletResponse)actionContext.get(org.apache.struts2.StrutsStatics.HTTP_RESPONSE);
if (TokenUtils.isVaild(request)) {
System.out.println("token 有效");
invocation.invoke();
}else{
System.out.println("token 過期");
JSONObject json = new JSONObject();
json.put("result", "002");
json.put("reason", "Token 過期");
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(json.toString());
response.getWriter().flush();
}
return null;
}
}
(4)struct2 配置
<!-- struts 設(shè)置默認(rèn)配置 -->
<package name="struts-shop" extends="struts-default">
<interceptors>
<!-- 默認(rèn)攔截器 -->
<interceptor name="authority" class="employee.utils.TokenInterceptor" />
<!-- 攔截器棧 -->
<interceptor-stack name="myStack">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="authority" />
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="myStack" />
</package>
<package name="/employee/application/action" namespace="/employee/application/action"
extends="struts-shop">
<action name="login" class="employee.application.action.AppUserManagerAction"
method="login" />
<action name="getPersonInfo" class="employee.application.action.AppUserManagerAction"
method="getPersonInfo"/>
</package>
4、總結(jié)
當(dāng)然在正式平臺,加密方式不會這么簡單,具體可以看下
http://blog.csdn.net/jack85986370/article/details/51362278 這篇文章,非對稱加密就有很強(qiáng)的加密方式,通過公鑰與私鑰進(jìn)行信息加密。有什么問題可以聯(lián)系筆者,歡迎技術(shù)交流哈~