java --webservice安全驗證

公司的項目完結了,總結下接口安全性問題java

webservice安全性驗證android

思路:web

1.移動端啓動app後請求的第一個接口是:獲取系統消息
算法

請求參數:無spring

請求頭部信息添加 "user-appid":"123456" 這個鍵值對。123456:移動端隨機生成的一個6位的數字。數組

2.客戶端請求 獲取系統消息接口,服務器這邊的處理spring-mvc

1>接收解析頭部消息安全

解析出user-appid的值,而後對其進行DES加密服務器

@RestController
public class SysInfoController {

    @Autowired
    private SysInfoService sysInfoServiceImpl;

    /**
     * 獲取系統信息
     * 
     * @return 系統信息
     */
    @RequestMapping(value = "/getSysInfo", method = RequestMethod.GET)
    public ResultObject getSysInfo(HttpServletRequest request) {
        
        System.out.println("請求路徑:/getSysInfo");
        ResultObject resultObject = new ResultObject();
        resultObject.setResultCode(ResultCode.SUCCESS);
        resultObject.setResultMsg(ResultMsg.MSG_SUCCESS);
        SysInfoRel sysInfoRel = sysInfoServiceImpl.getSysInfos();
        
        //獲取請求頭部信息
        Enumeration<String> headerNames = request.getHeaderNames();
        String key = "";
        String userToken = "";
        while (headerNames.hasMoreElements()) {
            key = (String) headerNames.nextElement();        
            if("user-appid".equals(key.toLowerCase())){
                try{
                    //對user-appid進行加密,算法是DES
                    userToken = DesUtil.encrypt(request.getHeader(key));
                }catch(Exception e){
                    sysInfoRel = null;
                    e.printStackTrace();
                }
                break;
            }
        }
        
        if (sysInfoRel != null && !StringUtils.isEmpty(userToken)) {
            sysInfoRel.setUserToken(userToken);
            resultObject.setData(sysInfoRel);
        } else {
            resultObject.setResultCode(ResultCode.FAILED);
            resultObject.setResultMsg(ResultMsg.MSG_FAILED);
        }
        return resultObject;
    }
}

2>將加密後的值(取名:userToken) 與 "系統消息"一塊兒返回給移動端mvc

3>之後,移動端每次請求本項目的其餘接口時,都在頭部信息中傳user-appid和userToken過來

4>服務器這端對userToken解密(DES解密算法),而後與user-appid進行比較

4-1>相等,則執行請求操做

4-2>不相等,則返回500錯誤

備註:步驟4>是在攔截器中進行的,攔截器代碼以下

package com.zhiji.caren.interceptor;

/**
 * 程序名         CRRequestInterceptor.java
 * 程序功能     MVC攔截器操做類
 * 做成者         xxx
 * 做成日期    2015-12-21
 * ======================修改履歷======================
 * 項目名                    狀態            做成者        做成日期
 * --------------------------------------------------
 * caren                新規            xxx        2015-12-21
 * =================================================
 */
import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.zhiji.caren.common.Constant;
import com.zhiji.caren.utils.DesUtil;

public class CRRequestInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        // TODO Auto-generated method stub
        
        //攔截器設置
        //0:關閉攔截器 --測試時使用
        //1:開啓攔截器 --發佈後使用
        //該方法返回true時,表示驗證經過,能夠執行請求接口操做
        if(Constant.switchFlag == 0){
            return true;
        }

        // 獲取訪問的頭部數據header
        String userAgent = "";
        String userToken = "";
        String userAppID = "";
        String specAppID = "com.cheqiren.cms";
        String contextPath = request.getPathInfo();
        // 默認未包含userToken
        boolean hasUserTokenFlag = false;
        boolean hasUserAppIDFlag = false;
        Enumeration<String> headerNames = request.getHeaderNames();
        //循環取頭部信息
        while (headerNames.hasMoreElements()) {
            String key = (String) headerNames.nextElement();
            if("user-agent".equals(key.toLowerCase())){
                userAgent = request.getHeader(key);
            }
            
            if("user-token".equals(key.toLowerCase())){
                hasUserTokenFlag = true;
                userToken = request.getHeader(key);
            }
            if("user-appid".equals(key.toLowerCase())){
                hasUserAppIDFlag = true;
                userAppID = request.getHeader(key);
            }

        }
        //所有經過後執行
        if(hasUserTokenFlag && hasUserAppIDFlag){
            // 容許後臺訪問接口
            if(specAppID.equals(userAppID) 
                    || contextPath.contains("getSysInfo")){
                return true;
            }
            // 訪問客戶端爲移動端時且受權碼符合規則,容許訪問接口
            //最後一項是對userToken進行解密
            if(userAgent.toLowerCase().contains("mobile") 
                    && (userAgent.toLowerCase().contains("iphone") 
                            //|| userAgent.toLowerCase().contains("iPad")
                            || userAgent.toLowerCase().contains("android"))
                    && userAppID.equals(DesUtil.decrypt(userToken))){
                    return true;
            }
        }
        response.sendError(500,"非法訪問!");
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // TODO Auto-generated method stub
    }
    
}

攔截器須要配置在spring-mvc文件中(關於這個配置文件,本博客其餘章節有詳細講解),配置以下

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**" />
            <!-- 定義在mvc:interceptor下面的表示是對特定的請求才進行攔截的 -->
            <bean class="com.zhiji.caren.interceptor.CRRequestInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>

最後,總結一下流程

1.移動端啓動app發起調用  獲取系統信息  請求

頭部消息包含

user-appid:隨機值

user-token:隨機值

2.進入攔截器

2.1攔截器取出user-appid值和user-token值

這兩個值同時true後,判斷路徑是否包含 getSysInfo

2.1.1包含,直接經過,去執行 獲取系統信息 接口的操做 --針對 獲取系統信息 接口

2.1.2不包含,則判斷是否是移動端發起的請求。--針對 本項目其餘接口

3.執行 獲取系統信息 接口

取出頭部信息user-appid,對其DES加密,傳給移動端

4.移動端請求其餘接口

user-appid:隨機值 --與請求獲取系統信息接口時傳的值同樣

user-token:用 系統信息接口返回的userToekn值替代

4.1 進入攔截器

4.1.1 取出user-appid值和user-token值

4.2.2 判斷訪問對象是否爲移動端 且 判斷受權碼(user-token)是否正確

4.2.3 正確,經過攔截器;不然,返回500錯誤

自此,流程結束。



以下部分講述的是DES算法,沒時間仔細研究,先貼上代碼

package com.zhiji.caren.utils;

/**
 * 程序名         DesUtil.java
 * 程序功能     DEC加密解密類
 * 做成者         xx
 * 做成日期    2016-01-11
 * ======================修改履歷======================
 * 項目名                    狀態            做成者        做成日期
 * --------------------------------------------------
 * caren                新規            xx        2016-01-11
 * =================================================
 */
import java.io.IOException;
import java.security.SecureRandom;
 
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
 
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
 
@SuppressWarnings("restriction")
public class DesUtil {
 
    private final static String DES = "DES";
    private final static String SEC_KEY = "readygo-tec.com";
 
    //測試使用
    public static void main(String[] args) throws Exception {
        String data = "DSD12345";
        System.err.println(encrypt(data));
        System.err.println(decrypt(encrypt(data)));
    }
     
    /**
     * Description 根據鍵值進行加密
     * @param data 
     * @param key  加密鍵byte數組
     * @return
     * @throws Exception
     */
    public static String encrypt(String data) throws Exception {
        byte[] bt = encrypt(data.getBytes(), SEC_KEY.getBytes());
        String strs = new BASE64Encoder().encode(bt);
        return strs;
    }
 
    /**
     * Description 根據鍵值進行解密
     * @param data
     * @param key  加密鍵byte數組
     * @return
     * @throws IOException
     * @throws Exception
     */
    public static String decrypt(String data) throws IOException,
            Exception {
        if (data == null)
            return null;
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] buf = decoder.decodeBuffer(data);
        byte[] bt = decrypt(buf,SEC_KEY.getBytes());
        return new String(bt);
    }
 
    /**
     * Description 根據鍵值進行加密
     * @param data
     * @param key  加密鍵byte數組
     * @return
     * @throws Exception
     */
    private static byte[] encrypt(byte[] data, byte[] key) throws Exception {
        // 生成一個可信任的隨機數源
        SecureRandom sr = new SecureRandom();
 
        // 從原始密鑰數據建立DESKeySpec對象
        DESKeySpec dks = new DESKeySpec(key);
 
        // 建立一個密鑰工廠,而後用它把DESKeySpec轉換成SecretKey對象
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
        SecretKey securekey = keyFactory.generateSecret(dks);
 
        // Cipher對象實際完成加密操做
        Cipher cipher = Cipher.getInstance(DES);
 
        // 用密鑰初始化Cipher對象
        cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
 
        return cipher.doFinal(data);
    }
     
     
    /**
     * Description 根據鍵值進行解密
     * @param data
     * @param key  加密鍵byte數組
     * @return
     * @throws Exception
     */
    private static byte[] decrypt(byte[] data, byte[] key) throws Exception {
        // 生成一個可信任的隨機數源
        SecureRandom sr = new SecureRandom();
 
        // 從原始密鑰數據建立DESKeySpec對象
        DESKeySpec dks = new DESKeySpec(key);
 
        // 建立一個密鑰工廠,而後用它把DESKeySpec轉換成SecretKey對象
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
        SecretKey securekey = keyFactory.generateSecret(dks);
 
        // Cipher對象實際完成解密操做
        Cipher cipher = Cipher.getInstance(DES);
 
        // 用密鑰初始化Cipher對象
        cipher.init(Cipher.DECRYPT_MODE, securekey, sr);
 
        return cipher.doFinal(data);
    }
}
相關文章
相關標籤/搜索