一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

spring boot架構(gòu)設(shè)計——權(quán)限驗證及API接口統(tǒng)一返回格式

 WindySky 2017-11-22

 

昨天奮戰(zhàn)了一天才搞定,記錄一下。

 

權(quán)限驗證

權(quán)限驗證實現(xiàn)需要截取request參數(shù),這個實現(xiàn)很簡單,springboot中可以使用interceptor,Aspect,filter實現(xiàn).具體實現(xiàn)網(wǎng)上一大把,就懶得寫了,關(guān)鍵字搜就是。

通過request獲取到請求參數(shù)后,按照自己定義的規(guī)則計算出sign值,例如把token+timestamp+邏輯方法參數(shù)字典排序后md5+base64位,然后和客戶端傳過來的sign值對比。

API接口統(tǒng)一返回格式

這個才是折騰人的玩意,在net下實現(xiàn)的時候覺得挺簡單的,在spring boot下做就要了老命了。

統(tǒng)一返回格式的目的是把邏輯方法和系統(tǒng)返回格式解耦合。例如API接口返回格式如下:

 

//權(quán)限驗證失敗返回
{
    "data": null,
    "code": 1,
    "msg": "服務(wù)器權(quán)限驗證失敗"
}
//發(fā)生未處理異常事件的返回
{
    "data": null,
    "code": -1,
    "msg": "服務(wù)器異常,請稍后再試"
}
復(fù)制代碼
//請求成功返回
{
    "data": {
        "id": 1,
        "userid": "test1",
        "name": "孫楊",
        "phone": "183*******",
        "sourcetype": 1,
        "loginname": "test1",
        "loginpwd": "test1",
        "powertype": 1,
        "registertime": "2017-10-19"
    },
    "code": 10000,
    "msg": ""
}
復(fù)制代碼

這種格式由2部分組成,第一部分就是最外層的data,code,msg。我稱之為系統(tǒng)級返回參數(shù),data里面的是方法級返回參數(shù)。處理邏輯是捕獲邏輯方法返回值,然后轉(zhuǎn)換為標準格式返回給客戶端。

spring boot的問題是interceptor不能獲取到返回值,restAPI方式的請求,ModelAndView這個參數(shù)返回的是null,也沒法從response參數(shù)里面取出返回值。google百度了一個下午,最后放棄。

然后選擇Aspect方式,利用round注解,很輕松的就拿到了返回值,一切看上去很順利,結(jié)果在修改返回值時,報錯了。。。因為產(chǎn)生響應(yīng)時,Aspect執(zhí)行順序在servlet前面,即邏輯方法返回model,然后Aspect執(zhí)行,然后servlet再執(zhí)行,而servlet默認會根據(jù)邏輯方法的返回值來對返回值進行序列化,這時候因為在Aspect中,我已經(jīng)修改了返回值類型,于是就會出現(xiàn)類型轉(zhuǎn)換錯誤。Aspect達不到目標,也宣告放棄。

最后看到外網(wǎng)一位網(wǎng)友建議使用filter。filter執(zhí)行順序在servlet之后,試了下,ok了,能捕獲,能修改。不過還有個問題,因為是在servlet之后執(zhí)行,而servlet默認是會把邏輯方法的返回值序列化的。。。于是data參數(shù)里面裝的就是一個字符串,然后filter返回的時候再序列化一次,data里面的數(shù)據(jù)格式就慘不忍睹了,客戶端還不罵死啊。夜深人懶,不想再累了,就直接把獲取到的邏輯方法的json字符串反序列化為object,然后裝配到data中,然后再把filter的參數(shù)序列化。。。最后接口返回的效果就是目前看的樣子,效率低,達到最低效果要求,日后有空再優(yōu)化吧。。。。。。下面貼下代碼

過濾器代碼:

復(fù)制代碼
/**
 * @author 孫楊
 * @date Created in 下午6:01 17/10/19
 */
@WebFilter(filterName = "全局過濾器", urlPatterns = "/*")
public class GlobalFilter implements Filter {

    private final String JsonArraySign = "[";
    private final boolean DEBUG = true;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        ResponseModel response = new ResponseModel();
        LogUtil logger = new LogUtil();
        StringBuilder sb = new StringBuilder();
        Gson gson = new GsonBuilder().serializeNulls().create();

        //權(quán)限校驗參數(shù)
        String token = "";
        String timestamp = "";
        String path = "";
        String sign = "";

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String[] paths = request.getRequestURL().toString().split("/api/sx");
        if (paths.length > 1) {
            path = paths[1];
        }

        long startTime = System.currentTimeMillis();
        sb.append("\n路徑: " + request.getRequestURL() + "\n");
        sb.append("method: " + request.getMethod() + "\n");
        sb.append("QueryString:  " + request.getQueryString() + "\n");
        sb.append("請求參數(shù):\n");
        Enumeration<String> paras = request.getParameterNames();
        while (paras.hasMoreElements()) {
            String name = paras.nextElement();
            String value = request.getParameter(name);
            if ("token".equals(name)) {
                token = value;
            }
            if ("timestamp".equals(name)) {
                timestamp = value;
            }
            if ("sign".equals(name)) {
                sign = value;
            }
            sb.append(name + ":" + value + "\n");
        }
        if (!DEBUG && !sign.equals(AuthVerificationUtil.countSign(token, timestamp, path))) {
            sb.append("方法耗時: " + (System.currentTimeMillis() - startTime) + "毫秒\n");
            response.setCode(CodeTable.ACCESSDENIED);
            response.setMsg("服務(wù)器權(quán)限驗證失敗");
            response.setData(null);
            sb.append("邏輯錯誤:" + "服務(wù)器權(quán)限驗證失敗: token:" + token +
                    " timestamp:" + timestamp + " path:" + path
                    + " 服務(wù)器端sign: " + AuthVerificationUtil.countSign(token, timestamp, path));
            logger.error(sb.toString());
        } else {
            MyResponseWrapper responseWrapper = new MyResponseWrapper((HttpServletResponse) servletResponse);
            try {
                filterChain.doFilter(servletRequest, responseWrapper);
                String responseContent = new String(responseWrapper.getDataStream());
                //判斷返回值是jsonObject還是jsonArray
                if (responseContent.startsWith(JsonArraySign)) {
                    response.setData(new JsonParser().parse(responseContent).getAsJsonArray());
                } else {
                    response.setData(new JsonParser().parse(responseContent).getAsJsonObject());
                }
                response.setCode(CodeTable.SUCCESS);
                response.setMsg("");
                sb.append("方法耗時: " + (System.currentTimeMillis() - startTime) + "毫秒\n");
                sb.append("響應(yīng)參數(shù): " + responseContent);
                logger.info(sb.toString());
            } catch (Exception e) {
                sb.append("方法耗時: " + (System.currentTimeMillis() - startTime) + "毫秒\n");
                if (e instanceof MyException) {
                    response.setCode(((MyException) e).getCode());
                    response.setMsg(((MyException) e).getMsg());
                    response.setData(null);
                    sb.append("邏輯錯誤:" + ((MyException) e).getLog());
                    logger.error(sb.toString());
                } else {
                    response.setCode(CodeTable.UNKNOWERROR);
                    response.setMsg("服務(wù)器異常,請稍后再試");
                    response.setData(null);
                    sb.append("未捕獲異常:" + e.getMessage());
                    logger.error(sb.toString());
                }
            }
        }
        ((HttpServletResponse) servletResponse).setHeader("Content-type", "application/json;charset=UTF-8");
        servletResponse.getOutputStream().write(gson.toJson(response).getBytes());
    }

    @Override
    public void destroy() {

    }
}
復(fù)制代碼

邏輯方法類似如下: 

復(fù)制代碼
    @RequestMapping("login")
    public User login(LoginModel loginModel) {
        User user = userRepository.findByLoginnameAndLoginpwd(loginModel.getLoginname(), loginModel.getLoginpwd());
        if (user == null) {
            throw new MyException(10001, "用戶名或密碼不對");
        }
        return user;
    }
復(fù)制代碼

折騰了這么多東西,目的就是徹底簡化邏輯方法代碼難度~定義好接口文檔和路徑,邏輯代碼編寫就可以丟給實習(xí)生了,又可以偷懶了~~

 

還有2個輔助類我也貼一下: 

 

復(fù)制代碼
public class MyResponseWrapper extends HttpServletResponseWrapper {
    ByteArrayOutputStream output;
    FilterServletOutputStream filterOutput;

    public MyResponseWrapper(HttpServletResponse response) {
        super(response);
        output = new ByteArrayOutputStream();
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        if (filterOutput == null) {
            filterOutput = new FilterServletOutputStream(output);
        }
        return filterOutput;
    }

    public byte[] getDataStream() {
        return output.toByteArray();
    }
}
復(fù)制代碼
復(fù)制代碼
public class FilterServletOutputStream extends ServletOutputStream {
    DataOutputStream output;

    public FilterServletOutputStream(OutputStream output) {
        this.output = new DataOutputStream(output);
    }

    @Override
    public void write(int arg0) throws IOException {
        output.write(arg0);
    }

    @Override
    public void write(byte[] arg0, int arg1, int arg2) throws IOException {
        output.write(arg0, arg1, arg2);
    }

    @Override
    public void write(byte[] arg0) throws IOException {
        output.write(arg0);
    }

    @Override
    public boolean isReady() {
        return false;
    }

    @Override
    public void setWriteListener(WriteListener writeListener) {

    }
}
復(fù)制代碼

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    中文字幕在线五月婷婷| 亚洲男人的天堂久久a| 国产视频在线一区二区| av中文字幕一区二区三区在线 | 国产综合欧美日韩在线精品| 99久久免费中文字幕| 日韩少妇人妻中文字幕| 亚洲一区二区精品免费视频| 国产欧美日韩综合精品二区| 精品久久综合日本欧美| 91精品视频全国免费| 欧美日韩一区二区综合| 欧美一区二区不卡专区| 国产精品欧美在线观看| 国产精品伦一区二区三区在线| 国产伦精品一区二区三区精品视频 | 好吊视频一区二区在线| 国产成人综合亚洲欧美日韩| 免费精品国产日韩热久久| 东京热电东京热一区二区三区| 国产熟女一区二区精品视频| 久一视频这里只有精品| 亚洲成人久久精品国产| 风间中文字幕亚洲一区| 久久精品中文字幕人妻中文| 日本精品中文字幕在线视频| 天海翼高清二区三区在线| 欧美综合色婷婷欧美激情| 丰满人妻一二区二区三区av| 99秋霞在线观看视频| 五月婷日韩中文字幕四虎| 亚洲精品日韩欧美精品| 欧美日韩精品久久亚洲区熟妇人| 国产一区国产二区在线视频| 欧美亚洲91在线视频| 狠狠干狠狠操亚洲综合| 国产日韩精品激情在线观看| 青青操日老女人的穴穴| 亚洲中文字幕亲近伦片| 99热九九在线中文字幕| 国产日韩欧美国产欧美日韩|