App接口之Token令牌实现

《App接口之Token令牌实现》
转载请注明来自 傻小孩b_移动开发http://www.jianshu.com/users/d388bcf9c4d3)喜欢的可以关注我,不定期总结文章!您的支持是我的动力哈!

1、目的

众所周知,在web端中,Token(令牌)只是作为防止用户重复提交表单的作用而存在。但是对于App客户端而言,Token却充当着另一种角色,类似现实生活中代表每个人的角色认证、或者类似浏览器cookie代表你访问网站的角色认证。前提,在有用户系统的应用中,在每次访问接口的时候,为了避免接口裸露被被无止境的请求攻击,往往我们会利用一种机制,过滤一切非应用用户端的非合法请求。首先我们不可能每次利用账号密码作为我们的过滤标准(会存在被抓包密码泄露风险),因此便有
Token(令牌)的存在。即在存在这里的Token是指在指定有效时间内可以代表用户角色,具有请求接口的权限。

当然,这里有开发者会提问,为什么不适用session。理论上是可以的,只是如果是有接触过这部分的移动开发者,session本地是不好处理的,并且完全依赖
session,会被黑客截取后模拟请求,依然会存在被攻击的风险。

2、实现思路

这里我不做加密方式选择的举例,这里只是做了简单的做了不可逆的MD5加密方式(账号+时间戳)。首先web框架是利用(spring + struct2 + mybatis),简单说明下实现思路:

1、用户登录。请求登录接口,如果账号密码核对正确,会根据账号和时间戳进行Md5加密生成Token

2、服务端双向保存token。服务端根据有效时间内生成对应的token之后,服务端双向保存了Memcache中(
Memcache 是一种分布式缓存存储机制,这里我就不详细说明了),最后通过登录接口返回Token信息至客户端中
3、客户端保存返回Token。
客户端通过登录接口成功返回的token,保存的内存中,在每次请求接口都要携带这个token,进行接口请求。

具体思路图如下所示:

token_1.png

token_2.png
3、案例

(1)登录接口实现

    /**
     * 用户登录
     * @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 生成对应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进行验证,这里是直接在
struct2中写了一个拦截器对请求接口用户Token的验证,有疑问的可以自己谷歌搜索下struct2自定义拦截器


/**
 * Token 拦截器 用于检测Token是否过去,过期直接不执行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();
            }
          
        }
        
        // 取得请求相关的ActionContext实例  
        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 设置默认配置 -->
    <package name="struts-shop" extends="struts-default">
        <interceptors>
            <!-- 默认拦截器 -->
            <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、总结

当然在正式平台,加密方式不会这么简单,具体可以看下
http://blog.csdn.net/jack85986370/article/details/51362278 这篇文章,非对称加密就有很强的加密方式,通过公钥与私钥进行信息加密。有什么问题可以联系笔者,欢迎技术交流哈~



作者:DevSiven
链接:https://www.jianshu.com/p/36e95d8dcabe
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值