文档章节

java --webservice安全验证

求是科技
 求是科技
发布于 2016/01/13 16:17
字数 1519
阅读 155
收藏 10

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

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



© 著作权归作者所有

求是科技

求是科技

粉丝 98
博文 451
码字总数 232301
作品 0
成都
后端工程师
私信 提问
加载中

评论(2)

求是科技
求是科技

引用来自“Javar”的评论

这个随机值有什么作用? 假如我抓包拿到了你的 随机值跟你 des加密过的串 我一样可以去请求接口了
这是初步的拦截,仅仅是拦截一般的普通人员,比如说我们项目组开发人员。还没考虑到网络抓包的问题,我们这个项目也不值得高手抓包。当然最终也是我们技术有限,欢迎大神提出更好的意见,谢谢!
深夜里的程序猿
深夜里的程序猿
这个随机值有什么作用? 假如我抓包拿到了你的 随机值跟你 des加密过的串 我一样可以去请求接口了
java 访问对方的WebService时,需要进行域验证?

我用java调用别人的webservice接口,但是老是通不过,报没有权限; 我现在的情况是: 1,这个webservice不知道用什么写的(这个应该不重要); 2,java怎么进行域验证? 3,我用cxf搭建了一个...

lifer
2013/03/22
1K
1
求助:ASP.NET Web程序调用JAVA WebService

本人在进行两个网站的集成,需要在需要在ASP.NET WEB程序中调用JAVA 开发的WebService。但是由于WebService采用的usernametoken验证方式,导致调用时总是报错(将WebService的验证方式去掉是...

何海清
2012/09/09
933
1
【小马哥】Spring Boot系列讲座

这里为大家推荐一个不错的Spring Boot系列讲座,讲师介绍如下: 小马哥,阿里巴巴技术专家,从事十余年Java EE 开发,国内微服务技术讲师。目前主要负责微服务技术推广、架构设计、基础设施、...

杜琪
2018/03/02
0
0
认识一下WebService应用的简单开发

在开发中,不免遇到两个不同系统交互的问题,比如数据信息同步、数据信息获取等,解决问题的途径有多种,今天带大家认识一下 WebService,看看它在实际应用中的处理。 一、什么是WebService ...

海岸线的曙光
2018/07/09
0
0
jaxws-webservice编程(第一个记录)

随着近几年来,SOA,EAI等架构体系的日渐成熟,Webservice越来越炽手可热,尤其是在企业做异质平台整合时成为了首选的技术。 Java的Webservice技术更是层出不穷,比较流行的有:Axis2,Sprin...

heroShane
2014/02/28
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Oracle SQL语法实例合集

如需转载请注明出处https://my.oschina.net/feistel/blog/3052024 目的:迅速激活Oracle SQL 参考:《Oracle从入门到精通》 ------------------------------------------------------------......

LoSingSang
今天
2
0
增加 PostgreSQL 服务进程的最大打开文件数

https://serverfault.com/questions/628610/increasing-nproc-for-processes-launched-by-systemd-on-centos-7 要在systemd的配置里加才行...

helloclia
今天
2
0
组合模式在商品分类列表中的应用

在所有的树形结构中最适合的设计模式就是组合模式,我们看看常用商品分类中如何使用。 先定义一个树形结构的商品接口 public interface TreeProduct { List<TreeProduct> allProducts(...

算法之名
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部