. Nampak dari nada jawapannya dia sedang menghafal karangan berkaki lapan
Jadi, untuk membolehkan semua orang merasai dengan mudah pelaksanaan idempoten antara muka, Saudara Tian menyusun artikel ini hari ini
. Artikel ini mempunyai sembilan kandungan utama:imdempotence, dalam istilah orang awam, adalah antara muka yang memulakan permintaan yang sama beberapa kali . Ia mesti dipastikan bahawa operasi hanya boleh dilaksanakan sekali sahaja, contohnya: Buat pengecam unik untuk setiap permintaan yang perlu memastikan hilang upaya
2. Penyelesaian biasa
keadaan mesin - keadaan perubahan, nilai status semasa mengemas kini data
3. Pelaksanaan artikel ini
Redis + token
4. Idea pelaksanaan
token
, 先获取token
, 并将此token
存入redis, 请求接口时, 将此token
放到header或者作为请求参数请求接口, 后端接口判断redis中是否存在此token
:
token
, maka, jika ia adalah permintaan berulang, disebabkan oleh token
telah dipadamkan, ia tidak boleh lulus pengesahan dan mengembalikan Sila jangan ulangi operasi
Prompttoken
, 那么, 如果是重复请求, 由于token
已被删除, 则不能通过校验, 返回请勿重复操作
提示
五、项目简介
Jika ia tidak wujud, ia bermakna parameter tersebut tidak sah atau ia adalah permintaan berulang, pulangkan sahaja prompt
Spring Boot
Redis
@ApiIdempotent
注解 + 拦截器对请求进行拦截@ControllerAdvice
全局异常处理Jmeter
🎜
5. Pengenalan Projek
Spring Boot
🎜🎜🎜🎜Redis
🎜🎜🎜🎜@ApiIdempotent
anotasi + pemintas memintas permintaan🎜🎜🎜🎜@ControllerAdvice
Pengendalian pengecualian global🎜🎜🎜🎜Alat ujian tekanan: Jmeter
🎜🎜🎜🎜Penjelasan:🎜🎜🎜Artikel ini memfokuskan pada pelaksanaan teras mati pucuk, tentang bagaimana Spring Boot Butiran penyepaduan redis, ServerResponse, ResponseCode dan butiran lain berada di luar skop artikel ini.🎜. warna latar belakang: rgba(27, 31, 35, 0.05); keluarga fon: 'Operator Mono', Consolas, Monaco, Menlo, monospace;patah perkataan: break-all;warna: rgb(239, 112, 96) ;">maven依赖<!-- Redis-Jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!--lombok 本文用到@Slf4j注解, 也可不引用, 自定义log即可-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
2、
JedisUtil
3、自定义注解@ApiIdempotent
/**
* 在需要保证 接口幂等性 的Controller的方法上使用此注解
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiIdempotent {
}
ApiIdempotentInterceptor
拦截器/**
* 接口幂等性拦截器
*/
public class ApiIdempotentInterceptor implements HandlerInterceptor {
@Autowired
private TokenService tokenService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
ApiIdempotent methodAnnotation = method.getAnnotation(ApiIdempotent.class);
if (methodAnnotation != null) {
check(request);// 幂等性校验, 校验通过则放行, 校验失败则抛出异常, 并通过统一异常处理返回友好提示
}
return true;
}
private void check(HttpServletRequest request) {
tokenService.checkToken(request);
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
TokenServiceImpl< /code></h2><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">@Service
public class TokenServiceImpl implements TokenService {
private static final String TOKEN_NAME = "token";
@Autowired
private JedisUtil jedisUtil;
@Override
public ServerResponse createToken() {
String str = RandomUtil.UUID32();
StrBuilder token = new StrBuilder();
token.append(Constant.Redis.TOKEN_PREFIX).append(str);
jedisUtil.set(token.toString(), token.toString(), Constant.Redis.EXPIRE_TIME_MINUTE);
return ServerResponse.success(token.toString());
}
@Override
public void checkToken(HttpServletRequest request) {
String token = request.getHeader(TOKEN_NAME);
if (StringUtils.isBlank(token)) {// header中不存在token
token = request.getParameter(TOKEN_NAME);
if (StringUtils.isBlank(token)) {// parameter中也不存在token
throw new ServiceException(ResponseCode.ILLEGAL_ARGUMENT.getMsg());
}
}
if (!jedisUtil.exists(token)) {
throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getMsg());
}
Long del = jedisUtil.del(token);
if (del <= 0) {
throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getMsg());
}
}
}</pre><div class="contentsignin">Salin selepas log masuk</div></div><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 1px;margin-bottom: 1px;">6、<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">TestApplication
maven
依赖
@SpringBootApplication @MapperScan("com.wangzaiplus.test.mapper") public class TestApplication extends WebMvcConfigurerAdapter { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } /** * 跨域 * @return */ @Bean public CorsFilter corsFilter() { final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); final CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowCredentials(true); corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); return new CorsFilter(urlBasedCorsConfigurationSource); } @Override public void addInterceptors(InterceptorRegistry registry) { // 接口幂等性拦截器 registry.addInterceptor(apiIdempotentInterceptor()); super.addInterceptors(registry); } @Bean public ApiIdempotentInterceptor apiIdempotentInterceptor() { return new ApiIdempotentInterceptor(); } }
2、JedisUtil
@RestController @RequestMapping("/token") public class TokenController { @Autowired private TokenService tokenService; @GetMapping public ServerResponse token() { return tokenService.createToken(); } }
3、自定义注解@ApiIdempotent
@RestController @RequestMapping("/test") @Slf4j public class TestController { @Autowired private TestService testService; @ApiIdempotent @PostMapping("testIdempotence") public ServerResponse testIdempotence() { return testService.testIdempotence(); } }
4、ApiIdempotentInterceptor
拦截器
5、TokenServiceImpl
6、TestApplication
好了,以上便是代码的实现部分,下面我们就来验证一下。
获取token
的控制器TokenController
rrreee
token
的控制器TokenController
:🎜@RestController @RequestMapping("/token") public class TokenController { @Autowired private TokenService tokenService; @GetMapping public ServerResponse token() { return tokenService.createToken(); } }
TestController
, 注意@ApiIdempotent
注解, 在需要幂等性校验的方法上声明此注解即可, 不需要校验的无影响:
@RestController @RequestMapping("/test") @Slf4j public class TestController { @Autowired private TestService testService; @ApiIdempotent @PostMapping("testIdempotence") public ServerResponse testIdempotence() { return testService.testIdempotence(); } }
获取token
:
查看Redis
:
测试接口安全性: 利用Jmeter
测试工具模拟50个并发请求, 将上一步获取到的token作为参数
header或参数均不传token, 或者token值为空, 或者token值乱填, 均无法通过校验, 如token值为abcd
。
Dalam gambar di atas, anda tidak boleh memadamkan token secara terus tanpa mengesahkan sama ada pemadaman akan berjaya atau tidak. kerana, Ada kemungkinan beberapa utas boleh mencapai baris 46 pada masa yang sama Pada masa ini, token belum dipadamkan, jadi pelaksanaan diteruskan Jika hasil pemadaman jedisUtil.del(token)
tidak disahkan dan dikeluarkan secara langsung, masalah berulang penyerahan masih akan berlaku, walaupun sebenarnya hanya terdapat operasi pemadaman sebenar, hasilkan semula di bawah. . Isu konkurensi, oleh itu, mesti disahkan
Atas ialah kandungan terperinci Penemuduga: Dalam antara muka pembayaran, wang hanya boleh ditolak sekali untuk pembayaran berulang untuk pesanan yang sama. Bagaimana untuk melakukan ini?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!