Java調(diào)用第三方接口封裝實(shí)現(xiàn)
介紹
在Java項(xiàng)目中,會(huì)遇到很多調(diào)用第三方接口的地方,比如接入微信,其他公司的系統(tǒng),需要傳token或者簽名。由于接入調(diào)用接口很多,每個(gè)接口內(nèi)部都需要手動(dòng)設(shè)置token或者其他數(shù)據(jù),這就顯得很麻煩。而且發(fā)送http請(qǐng)求還需要自己創(chuàng)建request對(duì)象。下面介紹2中封裝方式,
一、利用feign功能封裝請(qǐng)求,所有接口和服務(wù)之間調(diào)用是一樣的,只需要執(zhí)行后面的url,參數(shù)類(lèi),請(qǐng)求方式等。內(nèi)部需要傳輸?shù)膖oken信息,在自定的攔截器中設(shè)置,自定義的攔截器需要實(shí)現(xiàn)RequestInterceptor接口。在這里面設(shè)置公共的請(qǐng)求參數(shù)。比較推薦的。
二、自己封裝一個(gè)通用的請(qǐng)求類(lèi),里面自己創(chuàng)建Request對(duì)象,發(fā)送完http請(qǐng)求之后拿到返回的json對(duì)象在封裝返回類(lèi),借助于ObjectMapper類(lèi)。
一、借助feign實(shí)現(xiàn)調(diào)用
寫(xiě)一個(gè)feign的接口調(diào)用類(lèi),因?yàn)檎{(diào)用服務(wù)和接收服務(wù)不在一個(gè)注冊(cè)中心,所以需要指定url,并實(shí)現(xiàn)攔截器
自定義的feign接口
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient(
url = "${e-sign.apiHost}", // 這個(gè)值在配置文件中配置好
value = "authFeign" // 這個(gè)是自定義的服務(wù)名稱(chēng),沒(méi)有用
)
@RequestMapping(
headers = {"X-Tsign-Open-Auth-Mode=Signature"} // 設(shè)置請(qǐng)求的header
)
public interface EAuthFeign {
// 寫(xiě)封裝的參數(shù)和請(qǐng)求路徑
@PostMapping({"/v3/oauth2/index"})
EResponse<String> applyAuth(@RequestBody EApplyAuthDTO params);
}配置文件內(nèi)容
e-sign: projectId: 111111 projectSecret: fasdfads apiHost: https://ss.sign.cn callbackHost: https://call.baidu.com redirectHost: https://web.baidu.com
自定義攔截器,這個(gè)是feign的攔截器,只需要標(biāo)記被掃描到就行,會(huì)自動(dòng)加載。寫(xiě)了這個(gè)類(lèi)后,這個(gè)類(lèi)所在的項(xiàng)目所有的feign都會(huì)被這個(gè)攔截器控制
import common.exception.E;
import esign.config.ESignConfig;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
import java.util.Map;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import sun.misc.BASE64Encoder;
@Component
public class EAuthFeignRequestInterceptor implements RequestInterceptor {
private static final Logger log = LoggerFactory.getLogger(EAuthFeignRequestInterceptor.class);
@Autowired
private ESignConfig eSignConfig;
private static final BASE64Encoder encodeBase64 = new BASE64Encoder();
public EAuthFeignRequestInterceptor() {
}
public void apply(RequestTemplate template) {
String host = this.eSignConfig.getApiHost();
String url = template.url();
String target = template.feignTarget().url();
boolean b = target.equals(host);
// 這個(gè)判斷就是為了防止所有的feign都走這個(gè)攔截器,限制只有第三方接口才能進(jìn)入下面的邏輯
if (b) {
template.header("X-Tsign-Open-App-Id", new String[]{this.eSignConfig.getProjectId()});
Map<String, Collection<String>> headers = template.headers();
Collection<String> authMode = (Collection)headers.get("X-Tsign-Open-Auth-Mode");
boolean isSign = authMode != null && authMode.contains("Signature");
if (isSign) {
template.header("X-Tsign-Open-Ca-Timestamp", new String[]{String.valueOf(System.currentTimeMillis())});
template.header("Accept", new String[]{"*/*"});
template.header("Content-Type", new String[]{"application/json;charset=UTF-8"});
try {
this.setMd5AndSignature(template);
} catch (Exception var10) {
log.error("發(fā)送請(qǐng)求失敗, url:{}, params:{}", url, new String(template.body()));
throw new E("發(fā)送請(qǐng)求失敗");
}
} else {
template.header("X-Tsign-Open-Authorization-Version", new String[]{"v2"});
}
log.info("發(fā)送請(qǐng)求, url:{}, params:{}", url, template.body() == null ? "" : new String(template.body()));
}
}
public void setMd5AndSignature(RequestTemplate template) {
byte[] body = template.body();
String url = template.url();
String method = template.method();
String contentMD5 = doContentMD5(body);
if ("GET".equals(method) || "DELETE".equals(method)) {
contentMD5 = "";
url = URLDecoder.decode(url);
}
String message = appendSignDataString(method, "*/*", contentMD5, "application/json;charset=UTF-8", "", "", url);
log.info("接口,url:{}, 簽名字符串:{}", url, message);
String reqSignature = doSignatureBase64(message, this.eSignConfig.getProjectSecret());
template.header("Content-MD5", new String[]{contentMD5});
template.header("X-Tsign-Open-Ca-Signature", new String[]{reqSignature});
}
private static String doContentMD5(byte[] body) {
if (body == null) {
return "";
} else {
byte[] md5Bytes = null;
MessageDigest md5 = null;
String contentMD5 = null;
try {
md5 = MessageDigest.getInstance("MD5");
md5.update(body);
byte[] md5Bytes = md5.digest();
contentMD5 = encodeBase64.encode(md5Bytes);
return contentMD5;
} catch (NoSuchAlgorithmException var5) {
log.error("不支持此算法", var5);
throw new E("不支持此算法");
}
}
}
private static String doSignatureBase64(String message, String secret) {
String algorithm = "HmacSHA256";
String digestBase64 = null;
try {
Mac hmacSha256 = Mac.getInstance(algorithm);
byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
hmacSha256.init(new SecretKeySpec(keyBytes, 0, keyBytes.length, algorithm));
byte[] digestBytes = hmacSha256.doFinal(messageBytes);
digestBase64 = encodeBase64.encode(digestBytes);
return digestBase64;
} catch (NoSuchAlgorithmException var8) {
log.error("不支持此算法", var8);
throw new E("不支持此算法");
} catch (InvalidKeyException var9) {
log.error("無(wú)效的密鑰規(guī)范", var9);
throw new E("無(wú)效的密鑰規(guī)范");
}
}
private static String appendSignDataString(String method, String accept, String contentMD5, String contentType, String date, String headers, String url) {
StringBuilder sb = new StringBuilder();
sb.append(method).append("\n").append(accept).append("\n").append(contentMD5).append("\n").append(contentType).append("\n").append(date).append("\n");
if ("".equals(headers)) {
sb.append(headers).append(url);
} else {
sb.append(headers).append("\n").append(url);
}
return new String(sb);
}
}配置類(lèi)
@Configuration
@ConfigurationProperties(
prefix = "e-sign"
)
public class ESignConfig {
private static final Logger log = LoggerFactory.getLogger(ESignConfig.class);
private String projectId;
private String projectSecret;
private String apiHost;
private String callbackHost;
private String redirectHost;
}
上邊幾個(gè)類(lèi)就可以封裝對(duì)外請(qǐng)求了。自己服務(wù)根據(jù)要求可以另外處理
二、自己封裝請(qǐng)求類(lèi)
通用請(qǐng)求返回結(jié)果類(lèi)
package com.service;
import com.alibaba.fastjson.JSONObject;
import com.common.dto.coop.HttpDTO;
import com.common.vo.coop.ThirdResultVO;
import com.cooperation.generator.ThirdTokenGenerator;
import com.cooperation.template.TokenTemplate;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* 基于okhttp的請(qǐng)求客戶(hù)端的service
*
* @author aaa 2022-04-28
*/
@Slf4j
@Service
public class HttpService2 {
@Autowired
private TokenTemplate tokenTemplate;
/**
* 獲取接口調(diào)用返回值
*
* @param param 請(qǐng)求參數(shù)
* @author aaa 2022-04-28
*/
public ThirdResultVO getInterfaceResult(HttpDTO param) {
// 參數(shù)轉(zhuǎn)換成json
JSONObject json = Objects.isNull(param.getObj()) ? new JSONObject()
: JSONObject.parseObject(JSONObject.toJSONString(param.getObj()));
// 如果需要token,將token放在參數(shù)中
if (param.getNeedToken()) {
String token = tokenTemplate.getToken(new ThirdTokenGenerator());
json.put("token", token);
}
// 發(fā)起請(qǐng)求,獲得響應(yīng)
Response response = doRequest(param.getUrl(), json);
// 處理返回值
ThirdResultVO ThirdResult = handleReturn(response);
// 如果為null,響應(yīng)有問(wèn)題,不能轉(zhuǎn)成返回值類(lèi)型
Assert.notNull(ThirdResult, "接口調(diào)用異常");
return ThirdResult;
}
/**
* 處理返回值
*
* @param response 請(qǐng)求響應(yīng)
* @author aaa 2022-04-28
*/
private ThirdResultVO handleReturn(Response response) {
// 如果響應(yīng)為空,返回null
if (Objects.isNull(response)) {
return null;
}
try {
// 如果響應(yīng)體為空,返回null
if (Objects.isNull(response.body())) {
return null;
}
// 響應(yīng)體的字節(jié)數(shù)組
byte[] bytes = response.body().bytes();
log.info("調(diào)用接口返回?cái)?shù)據(jù)--:{}", new String(bytes));
// 轉(zhuǎn)換成返回值類(lèi)型
return JSONObject.parseObject(bytes, ThirdResultVO.class);
} catch (Exception e) {
log.info("處理返回值失敗:{}", e.getMessage(), e);
}
return null;
}
/**
* 發(fā)送請(qǐng)求
*
* @param url 請(qǐng)求地址
* @param jsonObject 參數(shù)DTO
* @author aaa 2022-04-28
*/
private Response doRequest(String url, JSONObject jsonObject) {
// 獲得表單請(qǐng)求對(duì)象
Request formRequest = getFormRequest(url, jsonObject);
// 調(diào)用接口 請(qǐng)求入?yún)?
log.info("調(diào)用接口請(qǐng)求入?yún)?-:url:{} , jsonObject :{}",url, JSONObject.toJSONString(jsonObject));
// 獲得okhttp請(qǐng)求工具
OkHttpClient requestClient = getRequestClient();
// 獲得請(qǐng)求調(diào)用對(duì)象
Call call = requestClient.newCall(formRequest);
// 發(fā)起請(qǐng)求,獲得響應(yīng)
Response execute = null;
try {
execute = call.execute();
} catch (IOException e) {
log.info("調(diào)用接口失?。簕}", e.getMessage(), e);
}
return execute;
}
/**
* 獲得表單請(qǐng)求
*
* @param url 請(qǐng)求地址
* @param jsonObject 參數(shù)DTO
* @author aaa 2022-04-28
*/
private Request getFormRequest(String url, JSONObject jsonObject) {
// 獲得請(qǐng)求構(gòu)造器
Request.Builder requestBuilder = new Request.Builder();
// 設(shè)置表單請(qǐng)求的請(qǐng)求頭
try {
requestBuilder.addHeader("content-type", "application/x-www-form-urlencoded").post(getFormBody(jsonObject)).url(new URL(url));
} catch (MalformedURLException e) {
log.info("url轉(zhuǎn)換失?。簕}", e.getMessage(), e);
}
// 返回請(qǐng)求對(duì)象
return requestBuilder.build();
}
/**
* 獲取請(qǐng)求客戶(hù)端
*
* @author aaa 2022-04-28
*/
private OkHttpClient getRequestClient() {
return new OkHttpClient.Builder()
.readTimeout(2L, TimeUnit.MINUTES)
.build();
}
/**
* 獲取form表單請(qǐng)求的body
*
* @param jsonObject 請(qǐng)求參數(shù)DTO
* @author aaa 2022-04-28
*/
private FormBody getFormBody(JSONObject jsonObject) {
// 獲取表單請(qǐng)求構(gòu)造器
FormBody.Builder fb = new FormBody.Builder();
// 設(shè)置請(qǐng)求參數(shù)
jsonObject.keySet().forEach(item -> fb.addEncoded(item, jsonObject.getString(item)));
// 返回body
return fb.build();
}
}通用請(qǐng)求參數(shù)類(lèi)
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class HttpDTO implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull(message = "請(qǐng)求路徑不能為空")
@NotBlank(message = "請(qǐng)求路徑不能為空")
@ApiModelProperty(value = "請(qǐng)求路徑url")
private String url;
@ApiModelProperty(value = "參數(shù)")
private Object obj;
@NotNull(message = "是否需要token 不能為空")
@ApiModelProperty(value = "是否需要token:true是,false否")
private Boolean needToken;
}請(qǐng)求調(diào)用示例
public List<String> getSkuCode(String classifyCode) {
ResultVO interfaceResult = httpService.getInterfaceResult(new HttpDTO()
.setUrl(Constant.HOST.concat(Constant.GOODS_GET_SKUS_BY_CLASSID_URI))
.setNeedToken(true)
.setObj(new DeLiGetSkusByClassId3DTO().setCode(classifyCode3).setIsAll(1)));
log.info(interfaceResult.getResultMessage());
return JSONArray.parseArray(interfaceResult.getResult(), String.class);
}
上邊的請(qǐng)求返回,從結(jié)果里取出result字符串,再轉(zhuǎn)一下list,這可以放在后面的請(qǐng)求方法中,傳一個(gè)返回的類(lèi)class。使用objectMapper轉(zhuǎn)一下。
private ObjectMapper mapper = new ObjectMapper();
public <T, RR> Response<RR> api(ApiDTO<T> jsonParams, Class<RR> responseResult) {
String json = JSONObject.toJSONString(jsonParams);
log.info("三方api.do, requestParams:{}", json);
String responseStr = this.thirdFileAgentFeign.api(json);
// 構(gòu)建返回類(lèi)型
JavaType javaType1 = this.mapper.getTypeFactory().constructParametricType(YZResponse.class, new Class[]{responseResult});
try {
// 這個(gè)代碼塊就看這里返回帶泛型的類(lèi)
return (Response)this.mapper.readValue(responseStr, javaType1);
} catch (JsonProcessingException e) {
log.error("反序列化返回值異常, responseStr:{}, errInfo:{}", new Object[]{responseStr, var7.getMessage(), e});
throw new E("反序列化返回值異常");
}
}第二種返回類(lèi)型
import com.fasterxml.jackson.core.type.TypeReference;
public static <T> T toObj(String json, Class<T> type) {
try {
return MAPPER.readValue(json, type);
} catch (IOException e) {
log.error("toObj error", e);
throw E.of(BaseExceptionEnum.FORMAT_ERROR);
}
}
public static <T> T toObj(String json, TypeReference<T> typeReference) {
try {
return mapper.readValue(json, typeReference);
} catch (IOException e) {
log.error("toObj error", e);
throw E.of(BaseExceptionEnum.FORMAT_ERROR);
}
}泛型返回處理可以參考RestTemplate里面的postForObject()
Class<?> deserializationView = ((MappingJacksonInputMessage)inputMessage).getDeserializationView();
if (deserializationView != null) {
// 部分代碼,構(gòu)建一個(gè)reader
ObjectReader objectReader = this.objectMapper.readerWithView(deserializationView).forType(javaType);
if (isUnicode) {
// 返回class類(lèi)型的對(duì)象
return objectReader.readValue(inputMessage.getBody());
}
Reader reader = new InputStreamReader(inputMessage.getBody(), charset);
return objectReader.readValue(reader);
}
具體可以學(xué)習(xí)一下ObjectMapper的用法
請(qǐng)求接口返回結(jié)果類(lèi)
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class ResultVO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "接口返回成功與否")
private Boolean success;
@ApiModelProperty(value = "返回?cái)?shù)據(jù)描述")
private String resultMessage;
@ApiModelProperty(value = "平臺(tái)業(yè)務(wù)代碼標(biāo)記")
private String resultCode;
// 返回的json結(jié)果,再處理成返回對(duì)象
@ApiModelProperty(value = "業(yè)務(wù)數(shù)據(jù)")
private String result;
}三、調(diào)用第三方生成token,可以使用策略類(lèi)實(shí)現(xiàn)
策略接口類(lèi)
public interface TokenGeneratorStrategy {
/**
* 獲取token
*
* @author aaa 2022-04-26
*/
String getToken();
/**
* 獲取token緩存key
*
* @author aaa 2022-04-26
*/
String getTokenCacheKey();
/**
* 獲取token過(guò)期時(shí)間,采用redis單位
*
* @author aaa 2022-04-26
*/
Duration getTokenExpireTime();
}獲取token的實(shí)現(xiàn)類(lèi)
package com.generator;
import com.alibaba.fastjson.JSONObject;
import com.common.constant.ThirdConstant;
import com.common.constant.MallCacheKeyConstant;
import com.common.constant.MallCommonConstant;
import com.common.dto.coop.HttpDTO;
import com.common.vo.coop.ThirdResultVO;
import com.common.vo.coop.ThirdTokenVO;
import com.cooperation.dto.ThirdTokenDTO;
import com.cooperation.service.HttpService;
import com.cooperation.strategy.TokenGeneratorStrategy;
import com.common.exception.E;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
/**
* 三方生成token
*
*/
@Slf4j
@Service
public class ThirdTokenGenerator implements TokenGeneratorStrategy {
@Autowired
private HttpService httpService;
@Override
public String getToken() {
// 獲取當(dāng)前時(shí)間,轉(zhuǎn)換成yyyy/MM/dd格式
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String format = LocalDateTime.now().format(dateTimeFormatter);
// 簽名規(guī)則:client_secret+timestamp+client_id+username+password+scope+client_secret將上面字符串MD5 加密后轉(zhuǎn)為小寫(xiě)。
String sign = ThirdConstant.CLIENT_SECRET
.concat(format)
.concat(ThirdConstant.CLIENT_ID)
.concat(ThirdConstant.USERNAME)
.concat(ThirdConstant.PASSWORD)
.concat(ThirdConstant.CLIENT_SECRET);
// md5 加密
String signMd5 = DigestUtils.md5DigestAsHex(sign.getBytes(StandardCharsets.UTF_8)).toLowerCase();
ThirdTokenDTO ThirdTokenDto = new ThirdTokenDTO().setClient_id(ThirdConstant.CLIENT_ID).setClient_secret(ThirdConstant.CLIENT_SECRET)
.setUsername(ThirdConstant.USERNAME).setPassword(ThirdConstant.PASSWORD)
.setTimestamp(format).setSign(signMd5);
// 接口調(diào)用
ThirdResultVO interfaceResult = httpService.getInterfaceResult(
new HttpDTO()
.setUrl(ThirdConstant.HOST.concat(ThirdConstant.TOKEN_URI))
.setObj(ThirdTokenDto)
.setNeedToken(false));
ThirdTokenVO ThirdToken = new ThirdTokenVO();
try {
ThirdToken = JSONObject.parseObject(interfaceResult.getResult(), ThirdTokenVO.class);
} catch (Exception e) {
log.info("token 返回值處理異常:{}", e.getMessage(), e);
}
if (Objects.isNull(ThirdToken)) {
log.error("獲取三方token為空----{}", interfaceResult);
throw new E("獲取三方token異常信息--" + interfaceResult.getResultMessage());
}
return ThirdToken.getAccess_token();
}
@Override
public String getTokenCacheKey() {
return MallCacheKeyConstant.COOP_Third_TOKEN + MallCommonConstant.DEFAULT_KEY;
}
/**
* 三方有效時(shí)間一小時(shí),這里的過(guò)期時(shí)間采用50分鐘
*/
@Override
public Duration getTokenExpireTime() {
// 當(dāng)前時(shí)間到12點(diǎn)的時(shí)間
Duration nowTo12 = Duration.between(LocalDateTime.now(), LocalDateTime.of(LocalDate.now(), LocalTime.MAX));
// 50分鐘
Duration fiftyMin = Duration.ofSeconds(50 * 60L);
// 如果當(dāng)前時(shí)間到12點(diǎn)的時(shí)間超過(guò)50分鐘,返回50分鐘
if (nowTo12.compareTo(fiftyMin) > 0) {
return fiftyMin;
}
// 否則返回當(dāng)前時(shí)間到12點(diǎn)的時(shí)間
return nowTo12;
}
}總結(jié)
到此這篇關(guān)于Java調(diào)用第三方接口封裝實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Java調(diào)用第三方接口封裝內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java調(diào)用第三方接口示范的實(shí)現(xiàn)
- 實(shí)例詳解Java調(diào)用第三方接口方法
- Java發(fā)送http請(qǐng)求調(diào)用第三方接口獲取token方式
- java后端如何調(diào)用第三方接口(往header和body中的參數(shù)傳參)
- 舉例說(shuō)明JAVA調(diào)用第三方接口的GET/POST/PUT請(qǐng)求方式
- java后端調(diào)用第三方接口返回圖片流給前端的具體代碼實(shí)現(xiàn)
- Java中調(diào)用第三方接口的幾種方法詳細(xì)指南
- Java中調(diào)用第三方接口的詳細(xì)代碼示例
相關(guān)文章
java隨機(jī)數(shù)生產(chǎn)算法實(shí)例
下面小編就為大家?guī)?lái)一篇java隨機(jī)數(shù)生產(chǎn)算法實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10
SpringBoot集成SpringSecurity安全框架方式
這篇文章主要介紹了SpringBoot集成SpringSecurity安全框架方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
Java實(shí)現(xiàn)按比抽獎(jiǎng)功能
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)按比抽獎(jiǎng)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-01-01
SpringBoot項(xiàng)目如何添加2FA雙因素身份認(rèn)證
雙因素身份驗(yàn)證2FA是一種安全系統(tǒng),要求用戶(hù)提供兩種不同的身份驗(yàn)證方式才能訪問(wèn)某個(gè)系統(tǒng)或服務(wù),國(guó)內(nèi)普遍做短信驗(yàn)證碼這種的用的比較少,不過(guò)在國(guó)外的網(wǎng)站中使用雙因素身份驗(yàn)證的還是很多的,這篇文章主要介紹了SpringBoot項(xiàng)目如何添加2FA雙因素身份認(rèn)證,需要的朋友參考下2024-04-04
Javaweb應(yīng)用使用限流處理大量的并發(fā)請(qǐng)求詳解
這篇文章主要介紹了Javaweb應(yīng)用使用限流處理大量的并發(fā)請(qǐng)求詳解,還是挺不錯(cuò)的,這里分享給大家,供需要的朋友參考。2017-11-11
Java使用JSONObject需要的6個(gè)jar包下載地址
這篇文章主要介紹了Java使用JSONObject需要的6個(gè)jar包下載地址,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
Java兩種方式實(shí)現(xiàn)動(dòng)態(tài)代理
Java 在 java.lang.reflect 包中有自己的代理支持,該類(lèi)(Proxy.java)用于動(dòng)態(tài)生成代理類(lèi),只需傳入目標(biāo)接口、目標(biāo)接口的類(lèi)加載器以及 InvocationHandler 便可為目標(biāo)接口生成代理類(lèi)及代理對(duì)象。我們稱(chēng)這個(gè)Java技術(shù)為:動(dòng)態(tài)代理2020-10-10

