Jadual Kandungan
JedisUtil%%PRE_BLOCK_1%%3、自定义注解@ApiIdempotent%%PRE_BLOCK_2%%4、ApiIdempotentInterceptor  拦截器%%PRE_BLOCK_3%%5、TokenServiceImpl< /code>" >2、JedisUtil%%PRE_BLOCK_1%%3、自定义注解@ApiIdempotent%%PRE_BLOCK_2%%4、ApiIdempotentInterceptor  拦截器%%PRE_BLOCK_3%%5、TokenServiceImpl< /code>
七、测试验证 " >七、测试验证
Rumah Java JavaSoalan temu bual Penemuduga: Dalam antara muka pembayaran, wang hanya boleh ditolak sekali untuk pembayaran berulang untuk pesanan yang sama. Bagaimana untuk melakukan ini?

Penemuduga: Dalam antara muka pembayaran, wang hanya boleh ditolak sekali untuk pembayaran berulang untuk pesanan yang sama. Bagaimana untuk melakukan ini?

Aug 22, 2023 pm 03:57 PM
soalan temuduga java

. 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:

1. Konsep idempotence

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:

  • Antaramuka pesanan, pesanan tidak boleh dibuat beberapa kali
  • Antara muka pembayaran, pembayaran berulang untuk pesanan yang sama hanya boleh ditolak sekali
  • Antara muka panggil balik Alipay, mungkin terdapat beberapa panggilan balik, panggilan balik berulang
    mesti diproses
  • Antara muka penyerahan borang biasa, disebabkan tamat masa rangkaian dan sebab lain, anda hanya boleh klik hantar beberapa kali, dan anda hanya boleh berjaya sekali dan seterusnya

2. Penyelesaian biasa

  1. Indeks unik--halang data kotor baharu
  2. mekanisme token--untuk mengelakkan penyerahan halaman berulang
  3. kunci pesimis--kunci apabila mendapatkan data (jadual kunci atau baris kunci)
  4. dilaksanakan-kunci-optimis berdasarkan nombor versi, semasa mengemas kini data Sahkan data dalam seketika
  5. Kunci teragih - redis (jedis, redisson) atau pelaksanaan penjaga zoo
keadaan mesin - keadaan perubahan, nilai status semasa mengemas kini data

3. Pelaksanaan artikel ini

Redis + token

🎜Artikel ini menggunakan cara kedua untuk melaksanakannya, iaitu melaksanakan pengesahan mati pucuk antara muka melalui mekanisme 🎜. 🎜

4. Idea pelaksanaan

Buat pengecam unik untuk setiap permintaan yang perlu memastikan hilang upayatoken, 先获取token, 并将此token存入redis, 请求接口时, 将此token放到header或者作为请求参数请求接口, 后端接口判断redis中是否存在此token:

  • Jika wujud, proses logik perniagaan seperti biasa dan padamkannya daripada redistoken, maka, jika ia adalah permintaan berulang, disebabkan oleh token telah dipadamkan, ia tidak boleh lulus pengesahan dan mengembalikan Sila jangan ulangi operasiPrompttoken, 那么, 如果是重复请求, 由于token已被删除, 则不能通过校验, 返回请勿重复操作提示
  • 如果不存在, 说明参数不合法或者是重复请求, 返回提示即可

五、项目简介

  • Spring Boot
  • Redis
  • @ApiIdempotent注解 + 拦截器对请求进行拦截
  • @ControllerAdvice全局异常处理
  • 压测工具: Jmeter
Jika ia tidak wujud, ia bermakna parameter tersebut tidak sah atau ia adalah permintaan berulang, pulangkan sahaja prompt

5. Pengenalan Projek

🎜Spring Boot🎜🎜🎜🎜Redis🎜🎜🎜🎜@ApiIdempotent anotasi + pemintas memintas permintaan🎜🎜🎜🎜@ControllerAdvicePengendalian 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
@Component
@Slf4j
public class JedisUtil {

    @Autowired
    private JedisPool jedisPool;

    private Jedis getJedis() {
        return jedisPool.getResource();
    }

    /**
     * 设值
     *
     * @param key
     * @param value
     * @return
     */
    public String set(String key, String value) {
        Jedis jedis = null;
        try {
            jedis = getJedis();
            return jedis.set(key, value);
        } catch (Exception e) {
            log.error("set key:{} value:{} error", key, value, e);
            return null;
        } finally {
            close(jedis);
        }
    }

    /**
     * 设值
     *
     * @param key
     * @param value
     * @param expireTime 过期时间, 单位: s
     * @return
     */
    public String set(String key, String value, int expireTime) {
        Jedis jedis = null;
        try {
            jedis = getJedis();
            return jedis.setex(key, expireTime, value);
        } catch (Exception e) {
            log.error("set key:{} value:{} expireTime:{} error", key, value, expireTime, e);
            return null;
        } finally {
            close(jedis);
        }
    }

    /**
     * 取值
     *
     * @param key
     * @return
     */
    public String get(String key) {
        Jedis jedis = null;
        try {
            jedis = getJedis();
            return jedis.get(key);
        } catch (Exception e) {
            log.error("get key:{} error", key, e);
            return null;
        } finally {
            close(jedis);
        }
    }

    /**
     * 删除key
     *
     * @param key
     * @return
     */
    public Long del(String key) {
        Jedis jedis = null;
        try {
            jedis = getJedis();
            return jedis.del(key.getBytes());
        } catch (Exception e) {
            log.error("del key:{} error", key, e);
            return null;
        } finally {
            close(jedis);
        }
    }

    /**
     * 判断key是否存在
     *
     * @param key
     * @return
     */
    public Boolean exists(String key) {
        Jedis jedis = null;
        try {
            jedis = getJedis();
            return jedis.exists(key.getBytes());
        } catch (Exception e) {
            log.error("exists key:{} error", key, e);
            return null;
        } finally {
            close(jedis);
        }
    }

    /**
     * 设值key过期时间
     *
     * @param key
     * @param expireTime 过期时间, 单位: s
     * @return
     */
    public Long expire(String key, int expireTime) {
        Jedis jedis = null;
        try {
            jedis = getJedis();
            return jedis.expire(key.getBytes(), expireTime);
        } catch (Exception e) {
            log.error("expire key:{} error", key, e);
            return null;
        } finally {
            close(jedis);
        }
    }

    /**
     * 获取剩余时间
     *
     * @param key
     * @return
     */
    public Long ttl(String key) {
        Jedis jedis = null;
        try {
            jedis = getJedis();
            return jedis.ttl(key);
        } catch (Exception e) {
            log.error("ttl key:{} error", key, e);
            return null;
        } finally {
            close(jedis);
        }
    }

    private void close(Jedis jedis) {
        if (null != jedis) {
            jedis.close();
        }
    }

}
3、自定义注解@ApiIdempotent
/**
 * 在需要保证 接口幂等性 的Controller的方法上使用此注解
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiIdempotent {
}
4、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 {
    }
}
5、TokenServiceImpl< /code></h2><pre class='brush:php;toolbar:false;'>@Service public class TokenServiceImpl implements TokenService { private static final String TOKEN_NAME = &quot;token&quot;; @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 &lt;= 0) { throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getMsg()); } } }</pre><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 拦截器

rrreee

5、TokenServiceImpl

rrreee

6、TestApplication

rrreee

好了,以上便是代码的实现部分,下面我们就来验证一下。

七、测试验证

获取token的控制器TokenControllerrrreee

好了,以上便是代码的实现部分,下面我们就来验证一下。🎜🎜🎜耶和华🎜🎜 🎜🎜🎜获取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

Penemuduga: Dalam antara muka pembayaran, wang hanya boleh ditolak sekali untuk pembayaran berulang untuk pesanan yang sama. Bagaimana untuk melakukan ini?

查看Redis

Penemuduga: Dalam antara muka pembayaran, wang hanya boleh ditolak sekali untuk pembayaran berulang untuk pesanan yang sama. Bagaimana untuk melakukan ini?

测试接口安全性: 利用Jmeter测试工具模拟50个并发请求, 将上一步获取到的token作为参数

Penemuduga: Dalam antara muka pembayaran, wang hanya boleh ditolak sekali untuk pembayaran berulang untuk pesanan yang sama. Bagaimana untuk melakukan ini?
Penemuduga: Dalam antara muka pembayaran, wang hanya boleh ditolak sekali untuk pembayaran berulang untuk pesanan yang sama. Bagaimana untuk melakukan ini?

header或参数均不传token, 或者token值为空, 或者token值乱填, 均无法通过校验, 如token值为abcd

Penemuduga: Dalam antara muka pembayaran, wang hanya boleh ditolak sekali untuk pembayaran berulang untuk pesanan yang sama. Bagaimana untuk melakukan ini?

8. Nota (sangat penting)

Penemuduga: Dalam antara muka pembayaran, wang hanya boleh ditolak sekali untuk pembayaran berulang untuk pesanan yang sama. Bagaimana untuk melakukan ini?

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

.

Baiklah, saya akan kongsikan di sini hari ini.

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!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

Video Face Swap

Video Face Swap

Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Artikel Panas

Skop pembolehubah PHP dijelaskan
4 minggu yang lalu By 百草
Petua untuk menulis komen php
4 minggu yang lalu By 百草
Mengulas kod dalam php
4 minggu yang lalu By 百草

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas

Tutorial PHP
1510
276
Penemubual: Anotasi biasa Spring Aop dan urutan pelaksanaan Penemubual: Anotasi biasa Spring Aop dan urutan pelaksanaan Aug 15, 2023 pm 04:32 PM

Anda mesti tahu Spring, jadi mari kita bincangkan tentang susunan semua pemberitahuan Aop Bagaimana Spring Boot atau Spring Boot 2 mempengaruhi susunan pelaksanaan aop? Beritahu kami tentang perangkap yang anda hadapi dalam AOP?

Temu bual dengan kumpulan tertentu: Jika anda menemui OOM dalam talian, bagaimanakah anda harus menyelesaikan masalah itu? Bagaimana untuk menyelesaikannya? Apakah pilihan? Temu bual dengan kumpulan tertentu: Jika anda menemui OOM dalam talian, bagaimanakah anda harus menyelesaikan masalah itu? Bagaimana untuk menyelesaikannya? Apakah pilihan? Aug 23, 2023 pm 02:34 PM

OOM bermakna terdapat kelemahan dalam program, yang mungkin disebabkan oleh kod atau konfigurasi parameter JVM. Artikel ini bercakap dengan pembaca tentang cara menyelesaikan masalah selepas proses Java mencetuskan OOM.

Soalan ujian bertulis Ele.me kelihatan mudah, tetapi ia membingungkan ramai orang Soalan ujian bertulis Ele.me kelihatan mudah, tetapi ia membingungkan ramai orang Aug 24, 2023 pm 03:29 PM

Jangan memandang rendah soalan peperiksaan bertulis banyak syarikat Terdapat perangkap dan anda boleh jatuh ke dalamnya secara tidak sengaja. Apabila anda menghadapi soalan ujian bertulis seperti ini tentang kitaran, saya cadangkan anda berfikir dengan tenang dan ambil langkah demi langkah.

Minggu lepas, saya ada temu bual dengan XX Insurance dan memang bagus! ! ! Minggu lepas, saya ada temu bual dengan XX Insurance dan memang bagus! ! ! Aug 25, 2023 pm 03:44 PM

Minggu lepas, seorang rakan dalam kumpulan pergi untuk temu bual dengan Ping An Insurance Hasilnya agak kesal, tetapi saya harap anda tidak akan berkecil hati, pada dasarnya semua soalan yang dihadapi temu duga boleh diselesaikan dengan menghafal soalan temuduga Ia telah diselesaikan, jadi sila bekerja keras!

5 Rentetan soalan temuduga, kurang daripada 10% orang boleh menjawab semuanya dengan betul! (dengan jawapan) 5 Rentetan soalan temuduga, kurang daripada 10% orang boleh menjawab semuanya dengan betul! (dengan jawapan) Aug 23, 2023 pm 02:49 PM

Artikel ini akan melihat 5 soalan temu bual tentang kelas Java String Saya sendiri telah mengalami beberapa daripada lima soalan ini semasa proses temu duga.

Novis juga boleh bersaing dengan penemuduga BAT: CAS Novis juga boleh bersaing dengan penemuduga BAT: CAS Aug 24, 2023 pm 03:09 PM

Bab tambahan bagi siri pengaturcaraan serentak Java, C A S (Banding dan tukar), masih dalam gaya yang mudah difahami dengan gambar dan teks, membolehkan pembaca berbual gila dengan penemuduga.

Adalah disyorkan untuk mengumpul 100 soalan temuduga Linux dengan jawapan Adalah disyorkan untuk mengumpul 100 soalan temuduga Linux dengan jawapan Aug 23, 2023 pm 02:37 PM

Artikel ini mempunyai jumlah lebih daripada 30,000 perkataan, meliputi gambaran keseluruhan Linux, cakera, direktori, fail, keselamatan, tahap sintaks, pertempuran praktikal, arahan pengurusan fail, arahan penyuntingan dokumen, arahan pengurusan cakera, arahan komunikasi rangkaian, arahan pengurusan sistem, sandaran arahan mampatan, dsb. Membongkar mata pengetahuan Linux.

Soalan yang ditanya dalam hampir semua wawancara Java: bercakap tentang perbezaan antara ArrayList dan LinkedList Soalan yang ditanya dalam hampir semua wawancara Java: bercakap tentang perbezaan antara ArrayList dan LinkedList Jul 26, 2023 pm 03:11 PM

Struktur data Java adalah tumpuan temu bual Sesiapa yang telah mengambil bahagian dalam temu bual Java mesti mempunyai pengalaman. Apabila penemuduga bertanya soalan sedemikian, mereka sering ingin menyemak sama ada anda telah mengkaji struktur asas jenis data yang biasa digunakan di Jawa, dan bukannya hanya kekal pada tahap "tahu cara menggunakan".

See all articles