springboot 完美对接RFC7616协议

springboot 快速对接RFC7616协议

前言在springboot实战开发的过程中,对于常用的安全认证方式有两种,一种是 basic 认证,一种是Digest认证,本编将为大家讲解Digest认证

一 、实现过程

1、客户端发起http请求,服务端第一次返回401,获取请求头:WWW-Authenticate信息

2、获取请求头,发起第二次请求返回200表示请求成功

二、代码讲解

1、客户端发起http请求,获取请求头:WWW-Authenticate信息

@Override
    public String digestAuth(String url) {
        try {
            String uri = dealUrl(url);
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setRequestMethod("GET");
            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
                String wwwAuthenticateHeader = connection.getHeaderField("WWW-Authenticate");
                String realm = extractHeaderValue(wwwAuthenticateHeader, "Digest realm");
                String nonce = extractHeaderValue(wwwAuthenticateHeader, "nonce");
                String opaque = extractHeaderValue(wwwAuthenticateHeader, "opaque");
                String qop = "auth";
                String nc = "00000002";
                String cnonce = generateNonce();
                String responseDigest = calculateResponseDigest(username, password, "GET", uri, realm, nonce,qop,nc,cnonce);
                StringBuilder digestBuilder = new StringBuilder();
                digestBuilder.append("username=\"").append(username).append("\", ");
                digestBuilder.append("realm=\"").append(realm).append("\", ");
                digestBuilder.append("nonce=\"").append(nonce).append("\", ");
                digestBuilder.append("uri=\"").append(uri).append("\", ");
                digestBuilder.append("response=\"").append(responseDigest).append("\", ");
                digestBuilder.append("opaque=\"").append(opaque).append("\", ");
                digestBuilder.append("qop=").append(qop).append(", ");
                digestBuilder.append("nc=").append(nc).append(", ");
                digestBuilder.append("cnonce=\"").append(cnonce).append("\"");
                return  "Digest " + digestBuilder.toString();
            }else{
                System.out.println("响应成功");
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    private String dealUrl(String url){
        // 获取第三个斜杠的下标
        int thirdSlashIndex = getThirdSlashIndex(url);
        System.out.println("第三个斜杠的下标:" + thirdSlashIndex);

        // 获取除去域名部分的后面路径
        String path = url.substring(thirdSlashIndex + 1);
        System.out.println("除去域名部分的后面路径:" + path);
        return path;
    }
  private  String calculateResponseDigest(String username, String password, String method, String url,
                                                  String realm, String nonce,String qop,String nc,String cnonce) {

        return calculateResponse(calculateHA1(username, realm, password),  nonce,qop, calculateHA2(method, url),nc,cnonce);
    }

    private  String calculateHA1(String username, String realm, String password) {
        String ha1 = username + ":" + realm + ":" + password;
        return calculateMD5Hash(ha1);
    }

    private  String calculateHA2(String method, String url) {
        String ha2 = method + ":" + url;
        return calculateMD5Hash(ha2);
    }

    private  String calculateResponse(String ha1, String nonce,String qop,String ha2,String nc,String cnonce) {
        String response = ha1 + ":" + nonce + ":" +nc  + ":" +cnonce+ ":" + qop + ":" + ha2;
        return calculateMD5Hash(response);
    }

    private  String calculateMD5Hash(String input) {
        return MD5(input);
    }

    private  String generateNonce() {
        // 在实际应用中,应该使用更安全的随机数生成方法
        int length = 16;
        String characters = "abcdefghijklmnopqrstuvwxyz0123456789";
        StringBuilder sb = new StringBuilder();

        Random random = new Random();
        for (int i = 0; i < length; i++) {
            int index = random.nextInt(characters.length());
            char randomChar = characters.charAt(index);
            sb.append(randomChar);
        }
        return sb.toString();
    }

    public  String MD5(String inStr) {
        MessageDigest md5 = null;

        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (Exception e) {
            System.out.println(e.toString());
            e.printStackTrace();
            return "";
        }

        char[] charArray = inStr.toCharArray();
        byte[] byteArray = new byte[charArray.length];

        for (int i = 0; i < charArray.length; i++) {
            byteArray[i] = (byte) charArray[i];
        }

        byte[] md5Bytes = md5.digest(byteArray);
        StringBuffer hexValue = new StringBuffer();

        for (int i = 0; i < md5Bytes.length; i++) {
            int val = ((int) md5Bytes[i]) & 0xff;
            if (val < 16)
                hexValue.append("0");
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString();
    }

    private String extractHeaderValue(String header, String key) {
        String[] parts = header.split(", ");
        for (String part : parts) {
            if (part.startsWith(key + "=\"")) {
                return part.substring(key.length() + 2, part.length() - 1);
            }
        }
        return null;
    }
     private static int getThirdSlashIndex(String url) {
        int count = 0;
        int index = -1;

        for (int i = 0; i < url.length(); i++) {
            if (url.charAt(i) == '/') {
                count++;
                if (count == 3) {
                    index = i;
                    break;
                }
            }
        }

        return index;
    }

2、客户端发起第二次请求


 @Override
    public String testApi(String url,String digest) {
        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setRequestMethod("GET");
            connection.setRequestProperty("Authorization",digest);
            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                // 处理服务端返回的数据
                BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String line;
                StringBuilder response = new StringBuilder();
                while ((line = reader.readLine()) != null) {
                    response.append(line).append("\n");
                }
                reader.close();
                connection.disconnect();
                System.out.println("Response: " + response.toString());
            } else {
                System.out.println("Authentication failed. Response code: " + responseCode);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

三、测试案例

1、控制层

@RestController
@RequestMapping("/apiUrl")
public class DhApiController {
    private static String addr =  "http://192.168.60.249";

    @Autowired
    private DhApiService dhApiService;

    @GetMapping("/getSerialNo")
    public Object getSerialNo() {
        String serialNo = dhApiService.getSerialNo(addr);
        return serialNo;
    }
}

2、接口层

/**
 *  http接口
 * @author yushu
 * @email 921784721@qq.com
 * @date 2024/2/22 8:47
 */
public interface DhApiService {

    /**
     * 获取设备序列号
     */
    String getSerialNo(String addr);

}

3、实现类


/**
 * 大华api实现类
 *
 * @author yushu
 * @email 921784721@qq.com
 * @date 2024/2/22 8:58
 */
@Service
public class DhApiServiceImpl implements DhApiService {
    private Logger logger = LoggerFactory.getLogger(DhApiServiceImpl.class);

    @Autowired
    private RfcService rfcService;

    /**
     * get请求 没有参数
     * @param addr
     * @param uri
     * @return
     */
    protected String getApi(String addr,String uri){
        final String url = addr+ uri;
        return rfcService.testApi(url, rfcService.digestAuth(url));
    }


    @Override
    public String getSerialNo(String addr) {

        return getApi(addr, DhApiEnum.GET_SERIAL_NO.getUri());
    }

}

4、工具类


/**
 * github地址 http://www.github.com/wanyushu
 * gitee地址 http://www.gitee.com/wanyushu
 *
 * @author yushu
 * @email 921784721@qq.com
 * @date 2024/2/21 14:52
 */
public interface RfcService {
    /**
     * 获取加密请求头
     * @return
     */
    String digestAuth(String url);
    /**
     * 获取接口信息
     */
    String testApi(String url,String digest);
}

package com.jiuzhou.desgin.rfc7616.impl;

import com.jiuzhou.desgin.rfc7616.RfcService;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.util.Random;

/**
 * github地址 http://www.github.com/wanyushu
 * gitee地址 http://www.gitee.com/wanyushu
 *
 * @author yushu
 * @email 921784721@qq.com
 * @date 2024/2/21 14:53
 */
@Service
public class RfcServiceImpl implements RfcService {

    String username = "admin";
    String password = "admin123";

    @Override
    public String digestAuth(String url) {
        try {
            String uri = dealUrl(url);
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setRequestMethod("GET");
            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
                String wwwAuthenticateHeader = connection.getHeaderField("WWW-Authenticate");
                String realm = extractHeaderValue(wwwAuthenticateHeader, "Digest realm");
                String nonce = extractHeaderValue(wwwAuthenticateHeader, "nonce");
                String opaque = extractHeaderValue(wwwAuthenticateHeader, "opaque");
                String qop = "auth";
                String nc = "00000002";
                String cnonce = generateNonce();
                String responseDigest = calculateResponseDigest(username, password, "GET", uri, realm, nonce,qop,nc,cnonce);
                StringBuilder digestBuilder = new StringBuilder();
                digestBuilder.append("username=\"").append(username).append("\", ");
                digestBuilder.append("realm=\"").append(realm).append("\", ");
                digestBuilder.append("nonce=\"").append(nonce).append("\", ");
                digestBuilder.append("uri=\"").append(uri).append("\", ");
                digestBuilder.append("response=\"").append(responseDigest).append("\", ");
                digestBuilder.append("opaque=\"").append(opaque).append("\", ");
                digestBuilder.append("qop=").append(qop).append(", ");
                digestBuilder.append("nc=").append(nc).append(", ");
                digestBuilder.append("cnonce=\"").append(cnonce).append("\"");
                return  "Digest " + digestBuilder.toString();
            }else{
                System.out.println("响应成功");
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private  String calculateResponseDigest(String username, String password, String method, String url,
                                                  String realm, String nonce,String qop,String nc,String cnonce) {

        return calculateResponse(calculateHA1(username, realm, password),  nonce,qop, calculateHA2(method, url),nc,cnonce);
    }

    private  String calculateHA1(String username, String realm, String password) {
        String ha1 = username + ":" + realm + ":" + password;
        return calculateMD5Hash(ha1);
    }

    private  String calculateHA2(String method, String url) {
        String ha2 = method + ":" + url;
        return calculateMD5Hash(ha2);
    }

    private  String calculateResponse(String ha1, String nonce,String qop,String ha2,String nc,String cnonce) {
        String response = ha1 + ":" + nonce + ":" +nc  + ":" +cnonce+ ":" + qop + ":" + ha2;
        return calculateMD5Hash(response);
    }

    private  String calculateMD5Hash(String input) {
        return MD5(input);
    }

    private  String generateNonce() {
        // 在实际应用中,应该使用更安全的随机数生成方法
        int length = 16;
        String characters = "abcdefghijklmnopqrstuvwxyz0123456789";
        StringBuilder sb = new StringBuilder();

        Random random = new Random();
        for (int i = 0; i < length; i++) {
            int index = random.nextInt(characters.length());
            char randomChar = characters.charAt(index);
            sb.append(randomChar);
        }
        return sb.toString();
    }

    public  String MD5(String inStr) {
        MessageDigest md5 = null;

        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (Exception e) {
            System.out.println(e.toString());
            e.printStackTrace();
            return "";
        }

        char[] charArray = inStr.toCharArray();
        byte[] byteArray = new byte[charArray.length];

        for (int i = 0; i < charArray.length; i++) {
            byteArray[i] = (byte) charArray[i];
        }

        byte[] md5Bytes = md5.digest(byteArray);
        StringBuffer hexValue = new StringBuffer();

        for (int i = 0; i < md5Bytes.length; i++) {
            int val = ((int) md5Bytes[i]) & 0xff;
            if (val < 16)
                hexValue.append("0");
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString();
    }

    private String extractHeaderValue(String header, String key) {
        String[] parts = header.split(", ");
        for (String part : parts) {
            if (part.startsWith(key + "=\"")) {
                return part.substring(key.length() + 2, part.length() - 1);
            }
        }
        return null;
    }

    private String dealUrl(String url){
        // 获取第三个斜杠的下标
        int thirdSlashIndex = getThirdSlashIndex(url);
        System.out.println("第三个斜杠的下标:" + thirdSlashIndex);

        // 获取除去域名部分的后面路径
        String path = url.substring(thirdSlashIndex + 1);
        System.out.println("除去域名部分的后面路径:" + path);
        return path;
    }

    private static int getThirdSlashIndex(String url) {
        int count = 0;
        int index = -1;

        for (int i = 0; i < url.length(); i++) {
            if (url.charAt(i) == '/') {
                count++;
                if (count == 3) {
                    index = i;
                    break;
                }
            }
        }

        return index;
    }

    @Override
    public String testApi(String url,String digest) {
        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setRequestMethod("GET");
            connection.setRequestProperty("Authorization",digest);
            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                // 处理服务端返回的数据
                BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String line;
                StringBuilder response = new StringBuilder();
                while ((line = reader.readLine()) != null) {
                    response.append(line).append("\n");
                }
                reader.close();
                connection.disconnect();
                System.out.println("Response: " + response.toString());
            } else {
                System.out.println("Authentication failed. Response code: " + responseCode);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
}

5、测试结果

1、第一次请求,返回401如图:

在这里插入图片描述

2、第二次请求返回200,需在请求填充第一次的算法结果

在这里插入图片描述

3、控制打印

在这里插入图片描述

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
前端对接 RFC 接口的方式通常是通过 HTTP 协议进行数据交互。以下是一个简单的示例,说明前端如何通过 HTTP 请求对接一个 RFC 接口: 1. 首先,前端需要了解目标 RFC 接口的请求格式和响应格式。根据接口规范,构造相应的请求参数,并将其转换为符合 HTTP 协议的格式。 2. 发送 HTTP 请求。可以使用浏览器提供的 XMLHttpRequest 对象或者第三方库(例如 axios)等工具发送 HTTP 请求。 3. 接收服务器响应。一旦服务器处理完请求,会返回相应的响应结果。前端可以通过相关的 API 获取服务器返回的数据。 4. 根据响应结果进行相应的处理。根据 RFC 接口规范,解析服务器返回的数据,并进行相应的处理。例如,将数据渲染到页面上,或者进行相应的逻辑判断等。 下面是一个使用 axios 库发送 HTTP 请求的示例: ```javascript // 构造请求参数 const requestData = { // 目标 RFC 接口的请求参数 }; // 发送 HTTP 请求 axios.post('http://example.com/api', requestData) .then(response => { // 解析服务器返回的数据 const responseData = response.data; // 根据响应结果进行相应的处理 // ... }) .catch(error => { // 处理请求错误 // ... }); ``` 需要注意的是,对接 RFC 接口需要遵循相应的规范和要求,否则可能会出现数据传输失败、数据解析错误等问题。因此,在对接 RFC 接口之前,需要仔细了解接口规范和要求,并根据实际情况进行开发。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值