java --webservice安全验证

公司的项目完结了,总结下接口安全性问题

webservice安全性验证

思路:

1.移动端启动app后请求的第一个接口是:获取系统消息

请求参数:无

请求头部信息添加 "user-appid":"123456" 这个键值对。123456:移动端随机生成的一个6位的数字。

2.客户端请求 获取系统消息接口,服务器这边的处理

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) 与 "系统消息"一起返回给移动端

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);
    }
}



你可能感兴趣的:(java --webservice安全验证)