• 基于Volley,Gson封装支持JWT无状态安全验证和数据防篡改的GsonRequest网络请求类


          这段时间做新的Android项目的client和和REST API通讯框架架构设计。使用了非常多新技术,终于的方案也相当简洁优雅。client仅仅须要传Java对象,server端返回json字符串,自己主动解析成Java对象, 无状态安全验证基于JWT实现,JWT规范的细节能够參考我前面的文章。

    JWT的token和数据防篡改签名统一放在HTTP Header中。这样就实现了对请求内容和返回结果的无侵入性,server端也能够在全局过滤器中统一处理安全验证。

          Androidclient使用了Volley网络请求框架和Gson解析库。基于这2个Goolge的框架封装了自己的GsonRequest网络请求基类。 网络请求类没有做特别处理,网络response返回消息,做了个带泛型的基类。统一处理错误码,消息和返回结果,Android端的代码例如以下:


    package com.zxt.network;
    
    /**
     * HTTP返回基类。返回正确结果status>0,错误结果status<0,
     * message为server端返回消息,client直接显示
     * T data为返回的json对象或数组,自己主动解析为Java对象
     * errorCodes为server端返回的Dubbo接口错误码。逗号分隔,仅做调试用途
     * Created by zhangxitao on 15/8/17.
     */
    public class BaseResponse<T> {
        public int status;
        public String message;
        public String errorCodes;
        public T data ;
    
        public int getStatus() {
            return status;
        }
    
        public void setStatus(int status) {
            this.status = status;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        public String getErrorCodes() {
            return errorCodes;
        }
    
        public void setErrorCodes(String errorCodes) {
            this.errorCodes = errorCodes;
        }
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    }
    

    package com.zxt.network;
    
    import android.text.TextUtils;
    import android.util.Log;
    
    import com.android.volley.AuthFailureError;
    import com.android.volley.NetworkResponse;
    import com.android.volley.ParseError;
    import com.android.volley.Request;
    import com.android.volley.Response;
    import com.android.volley.Response.ErrorListener;
    import com.android.volley.Response.Listener;
    import com.android.volley.toolbox.HttpHeaderParser;
    import com.google.gson.Gson;
    import com.qianmi.qmpos.AppConfig;
    
    import java.io.UnsupportedEncodingException;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * Gson请求类,传入请求对象和返回类名,返回解析好的对象,封装JWT安全验证,数据签名
     * @param <T>
     */
    public class GsonRequest<T> extends Request<T> {
    
        private static final String TAG = "GsonRequest";
    
        private final Listener<T> mListener;
    
        private Gson mGson;
    
        private Class<T> mClass;
        private Object mRequest;
        private String mBody;
    
        public GsonRequest(int method, String url, Object request, Class<T> clazz, Listener<T> listener, ErrorListener errorListener) {
            super(method, url, errorListener);
            mGson = new Gson();
            mClass = clazz;
            mListener = listener;
            mRequest = request;
            if (null != request) {
                mBody = mGson.toJson(mRequest);
            }
        }
    
        public GsonRequest(String url, Object request, Class<T> clazz, Listener<T> listener, ErrorListener errorListener) {
            this(Method.POST, url, request, clazz, listener, errorListener);
            Log.d(TAG, "-----url is:" + url);
        }
    
        @Override
        public byte[] getBody() throws AuthFailureError {
            if (null == mRequest) {
                return null;
            }
            Log.d(TAG, "-----request json: " + mBody);
            return mBody.getBytes();
        }
    
        @Override
        protected Response<T> parseNetworkResponse(NetworkResponse response) {
            try {
                String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
                return Response.success(mGson.fromJson(jsonString, mClass), HttpHeaderParser.parseCacheHeaders(response));
            } catch (UnsupportedEncodingException e) {
                return Response.error(new ParseError(e));
            }
        }
    
        @Override
        protected void deliverResponse(T response) {
            mListener.onResponse(response);
            Log.i(TAG, "-----response:" + new Gson().toJson(response));
        }
    
        @Override
        public Map<String, String> getHeaders() throws AuthFailureError {
            HashMap<String, String> extraHeaders = new HashMap<>();
            extraHeaders.put("Content-Type", "application/json");
            extraHeaders.put("Accept", "application/json");
    
            //Data Sign
            if (!TextUtils.isEmpty(mBody)) {
                extraHeaders.put("Sign", MD5Util.stringToMD5(AppConfig.SIGN_SECRET + mBody + <span style="font-family: Arial, Helvetica, sans-serif;">AppConfig.SIGN_SECRET </span><span style="font-family: Arial, Helvetica, sans-serif;">));</span>
            }
            //JWT token
            if (!TextUtils.isEmpty(AppConfig.TOKEN)) {
                extraHeaders.put("Authorization", "Bearer " + AppConfig.TOKEN);
            }
            Log.d(TAG, "-----extra headers: " + extraHeaders.toString());
    
            return extraHeaders;
        }
    }
    

    一个简单的调用样例

      LoginRequest loginRequest = new LoginRequest();
     
            loginRequest.setNickName(username);
            loginRequest.setPassword(RSAUtils.getEncryptPwd(password));
    
            Request request = new GsonRequest<LoginResponse>(
                   AppConfig.SERVER_URL + "/login",loginRequest, LoginResponse.class,
                    new Response.Listener<LoginResponse>() {
                        @Override
                        public void onResponse(LoginResponse resp) {
                            L.d("TAG", "user is " + resp.data);
                            //TODO 
    
                        }
                    }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    Log.e("TAG", error.getMessage(), error);
                }
            });
    mVolleyQueve.add(request)
    
    


    server端使用Spring Boot,Spring MVC。 JPA。JWT等技术构建。相同的简洁优雅, Spring Boot微服务技术确实简化了接口的开发,能够做到零XML配置文件。因为临时权限这边不够复杂。临时没有引入Spring Security框架,安全验证通过一个全局的过滤器完毕。API接口通过使用@RestController注解实现。详细的參考代码例如以下:


    import com.zxt.domain.BaseResponse;
    import com.zxt.domain.pos.Device;
    import com.zxt.service.DeviceService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/api/device")
    public class DeviceController {
    
        @Autowired
        DeviceService deviceService;
    
        @RequestMapping("activate")
        @ResponseBody
        public BaseResponse activate(@RequestBody @Validated Device device) {
    
            try {
                this.deviceService.activateDevice(device);
            } catch (Exception e) {
                return new BaseResponse(-1,"activate failed");
            }
    
            return new BaseResponse(1,"device activated");
        }
    
    }
    

    Google Volley这个框架除了攻克了频繁网络请求情景的性能问题之外。可定制性也是非常强大的,通过定制我们自己的GsonRequest类,实现了对数据传输无侵入性的JWT安全验证和数据防篡改, 双向自己主动化的JSON解析能力,极大的简化了Android网络App的开发工作。



  • 相关阅读:
    Android应用程序执行流程
    Android的架构与Android应用程序启动流程
    Android开发环境使用工具Eclipse IDE工程目录结构
    MySQL 的 crash-safe 原理解析
    vivo 悟空活动中台
    图解 Promise 实现原理(三)—— Promise 原型方法实现
    领域驱动设计(DDD)实践之路(三):如何设计聚合
    深入浅出开源监控系统Prometheus(上)
    你还应该知道的哈希冲突解决策略
    反应式编程 RxJava 设计原理解析
  • 原文地址:https://www.cnblogs.com/clnchanpin/p/6893735.html
Copyright © 2020-2023  润新知