http digest認證(Java server)

背景:服務器接收客戶端請求,處理並驗證。並返回服務器的驗證結果。
關於digest認證的相關概念及驗證原理查看相關的說明,此處只對處理進行貼碼。

CODE:

import com.alibaba.fastjson.JSONObject;
import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;

@Slf4j
@Component
public class DigestDao {
    @Value("${registerUserName}")
    String registerUserName;
    @Value("${registerPassWord}")
    String registerPassWord;
    @Value("${platformId}")
    String platformId;
    private static final String DIGEST = "Digest ";
    private static final String NONCE = "nonce";
    private static final String QOP = "qop";
    private static final String REALM = "realm";
    private static final String NC = "nc";
    private static final String CNONCE = "cnonce";
    private static final String RESPONSE = "response";
    private static final String URI = "uri";
    private static final String HEX_LOOKUP = "0123456789abcdef";

    private static final String realm = "viid";
    private static final String qop = "auth";

    private static final Charset defaultChatSet;
    private static final MessageDigest md5;

    static {
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            throw new NullPointerException("MD5");
        }
        defaultChatSet = StandardCharsets.ISO_8859_1;
    }

    /**
     *  HTTP Digest驗證
     * @param httpServletRequest
     * @param httpResponse
     * @param url
     * @param type
     * @return
     */
    public String auth(HttpServletRequest httpServletRequest, HttpServletResponse httpResponse, String url,
                       String type) {
        String authorization = httpServletRequest.getHeader("Authorization");
        log.info("authorization:{}", authorization);
        if (authorization != null) {
            if (authorization.startsWith(DIGEST.trim())) {
                HashMap<String, String> authFields = splitAuthFields(authorization.substring(7));
                String newResponse = authFields.get(RESPONSE);
                String A1 = calcDigest(registerUserName, authFields.get(REALM),
                        registerPassWord);//A1 = MD5("usarname:realm:password");
                String A2 = calcDigest(HttpMethod.POST.toString(), authFields.get(URI));//A2 = MD5("httpmethod:uri");
                String oriResponse = calcDigest(A1, authFields.get(NONCE), authFields.get(NC), authFields.get(CNONCE),
                        authFields.get(QOP), A2); //response = MD5("A1:nonce:nc:cnonce:qop:A2");
                if (oriResponse.equals(newResponse)) {
                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYYMMddHHmmss");
                    httpResponse.setStatus(HttpStatus.SC_OK);
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("RequestURL", url);
                    jsonObject.put("StatusString", type + " success");
                    jsonObject.put("StatusCode", "0");
                    jsonObject.put("Id", platformId);
                    jsonObject.put("LocalTime", simpleDateFormat.format(new Date()));

                    JSONObject jsonObject1 = new JSONObject();
                    jsonObject1.put("ResponseStatusObject", jsonObject);
                    log.info(type + "success:{}", jsonObject1.toJSONString());

                    return jsonObject1.toJSONString();
                } else {
                    log.info(type + " failed!");
                    httpResponse.setStatus(HttpStatus.SC_UNAUTHORIZED);
                }
            } else {
                log.info("返回的非摘要認證數據!");
            }
        } else {
            httpResponse.setStatus(HttpStatus.SC_UNAUTHORIZED);
            httpResponse.setHeader("WWW-Authenticate", getAuthenticate());
        }
        return "";
    }

    private String getAuthenticate(){
        return "Digest " + "realm=" + realm + ",nonce=" + getNonce() + ",qop=" + qop;
    }

    private String getNonce(){
        md5.reset();
        md5.update(("test" + System.currentTimeMillis()).getBytes(defaultChatSet));
        return bytesToHexString(md5.digest());
    }

    private String calcDigest(String first, String ... args){
        StringBuilder stringBuilder = new StringBuilder(first);
        for (String str : args){
               stringBuilder.append(':').append(str);
        }
        md5.reset();
        md5.update(stringBuilder.toString().getBytes());
        return bytesToHexString(md5.digest());
    }

    private static HashMap<String, String> splitAuthFields(String authString) {
        final HashMap<String, String> fields = Maps.newHashMap();
        final CharMatcher trimmer = CharMatcher.anyOf("\"\t ");
        final Splitter commas = Splitter.on(',').trimResults().omitEmptyStrings();
        final Splitter equals = Splitter.on('=').trimResults(trimmer).limit(2);
        String[] valuePair;
        for (String keyPair : commas.split(authString)) {
            valuePair = Iterables.toArray(equals.split(keyPair), String.class);
            fields.put(valuePair[0], valuePair[1]);
        }
        return fields;
    }

    private static String bytesToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            sb.append(HEX_LOOKUP.charAt((bytes[i] & 0xF0) >> 4));
            sb.append(HEX_LOOKUP.charAt((bytes[i] & 0x0F) >> 0));
        }
        return sb.toString();
    }
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章