On What a Hassle Login Is UPDATING

Everyone who has written a backend, sooner or later, has to fight a hard battle with “login.” It looks like a small feature — a username, a password, a button — but anyone who has actually built it knows this is the part of the whole system with the deepest pits, the most painful changes, and the most fatal failures. It’s a hassle not because the technology is hard, but because it sits right at the intersection of three parties’ interests: users want convenience, product wants growth, and security wants an airtight defense. These three are naturally at odds. Every inch you move toward one, the other two start crying out. ...

2025-12-26 03:56:40 PM · 13 min

Handy Spring Boot Code Snippets UPDATING

General Dependency management Pinning all three versions in the parent pom.xml Version reference <dependencyManagement> <dependencies> <!-- 1. Spring Boot base version --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.5.0</version> <type>pom</type> <scope>import</scope> </dependency> <!-- 2. Spring Cloud cloud-native base --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2025.0.0</version> <type>pom</type> <scope>import</scope> </dependency> <!-- 3. Spring Cloud Alibaba full dependency core (key!) --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2025.0.0.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> Main class @MapperScan("asia.liminality.user.mapper") @SpringBootApplication @Slf4j public class UserApplication { public static void main(String[] args) throws UnknownHostException { ConfigurableApplicationContext app = SpringApplication.run(UserApplication.class, args); Environment env = app.getEnvironment(); String protocol = "http"; if (env.getProperty("server.ssl.key-store") != null) { protocol = "https"; } log.info("--/\n---------------------------------------------------------------------------------------\n\t" + "Application '{}' is running! Access URLs:\n\t" + "Local: \t\t{}://localhost:{}\n\t" + "External: \t{}://{}:{}\n\t" + "Profile(s): \t{}" + "\n---------------------------------------------------------------------------------------", env.getProperty("spring.application.name"), protocol, env.getProperty("server.port"), protocol, InetAddress.getLocalHost().getHostAddress(), env.getProperty("server.port"), env.getActiveProfiles()); } } domain DTO Result import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class Result<T> { private int code; private String message; private T data; public static <T> Result<T> success(T data) { return new Result<>(200, "success", data); } public static <T> Result<T> success() { return new Result<>(200, "success", null); } public static <T> Result<T> failure(String message) { return new Result<>(500, message, null); } public static <T> Result<T> failure(int code, String message) { return new Result<>(code, message, null); } public static <T> Result<T> failure(String message, T data) { return new Result<>(500, message, data); } } Spring Cloud Gateway AuthGlobalFilter @Slf4j @RequiredArgsConstructor @Component public class AuthGlobalFilter implements GlobalFilter, Ordered { private final JwtUtil jwtUtil; private static final List<String> WHITE_LIST = List.of( "/auth/user/login", "/auth/user/register" ); // Check whether it's in the whitelist private boolean isWhiteList(String path){ return WHITE_LIST.stream().anyMatch(path::startsWith); } private Mono<Void> reject(ServerWebExchange exchange){ exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); String path = request.getPath().toString(); if (isWhiteList(path)) { return chain.filter(exchange); } String token = request.getHeaders().getFirst("Authorization"); // Token is empty, deny access if( token == null || token.isEmpty() ){ return reject( exchange ); } // Verify the token try { Integer userId = jwtUtil.parseToken(token); // TODO put userId into the request header log.info("🚪 inject into request header"); } catch (Exception e) { return reject(exchange); } return chain.filter( exchange ); } // Filter priority; the smaller, the earlier @Override public int getOrder() { return -1; } }

2025-11-26 09:51:37 AM · 2 min

Sudden Sensorineural Hearing Loss Treatment Process

September 22, Monday — Day 1: Sudden onset Woke up in the morning with a blocked feeling in the right ear; the left ear was normal. I assumed it was just ear congestion or possibly otitis media, something that would resolve on its own, so I didn’t pay much attention. September 23, Tuesday — Day 2 In addition to the blocked sensation, I noticed an echo when hearing external speech. I started to feel something was wrong, but I still didn’t think it was serious and prioritized work. ...

2025-11-12 12:24:27 PM · 4 min

The Incompatibility Between PostgreSQL and Java's LocalDateTime UPDATING

Java’s LocalDateTime and PostgreSQL’s time types “aren’t talking about the same kind of time.” First, get clear on PostgreSQL’s two time types TIMESTAMP → without time zone, just a bare time "2026-06-25 10:00:00" TIMESTAMPTZ → with time zone, stored internally as UTC, converted to the session time zone on query On the Java side LocalDateTime → no concept of a time zone, just a bare time ZonedDateTime → with time zone OffsetDateTime → with an offset (e.g. +08:00) Instant → a UTC timestamp Why the error happens The PostgreSQL JDBC driver (especially newer versions 42.x+) is very strict about type matching: ...

2025-10-25 12:55:49 PM · 2 min
Backend Troubleshooting

Swagger 2 vs. Swagger 3 Annotation Comparison Table UPDATING

In the Spring Boot ecosystem, the annotations changed a great deal between Swagger 2.0 (typically using the Springfox dependency) and Swagger 3.0 (typically using the Springdoc-openapi dependency, based on the OpenAPI 3 spec). Below is a complete correspondence table of the common annotations in Swagger 2.0 vs. Swagger 3.0 (OpenAPI 3): 1. Core annotation correspondence table Description Swagger 2.0 annotation (io.swagger.annotations) Swagger 3.0 annotation (io.swagger.v3.oas.annotations) Notes Mark a controller class @Api(tags = "User API") @Tag(name = "User API") 3.0 removed the description attribute; use name uniformly Mark an API method @ApiOperation(value = "Get user") @Operation(summary = "Get user") In 3.0 value became summary Request/entity class @ApiModel(value = "User object") @Schema(description = "User object") 3.0 greatly simplified this; use @Schema uniformly Entity class property @ApiModelProperty(value = "Name") @Schema(description = "Name") As above, merged into @Schema Ignore a property @ApiModelProperty(hidden = true) @Schema(hidden = true) Ignore a whole class/method @ApiIgnore @Hidden For endpoints or parameters you don’t want exposed in docs 2. Request-parameter annotation correspondence table For method parameters (URL path parameters, query parameters, etc.), 3.0 introduced a more structured configuration: ...

2025-09-25 09:06:35 AM · 2 min
Backend Troubleshooting

SCA 2023.X — Troubleshooting and Fixing the Nacos Bootstrap Config Failure

Issue source: spring-cloud-alibaba#3931 Affected versions: spring-cloud-alibaba 2023.0.1.3+ 1. Problem Description In Spring Cloud Alibaba 2023.X, placing Nacos config (including extension-configs, shared-configs, etc.) in bootstrap.yml / bootstrap.properties causes the config center’s content to fail to load properly, even though the logs show the bootstrap file itself was read. Moving the same config to application.yml makes everything work again. 2. Root Cause flowchart TD A[bootstrap.yml is read] --> B{SCA version check} B -- 2023.0.1.2 and earlier --> C[✅ Nacos config center loads normally] B -- 2023.0.1.3 and later --> D[❌ extension-configs / shared-configs fail] D --> E[Root cause: SCA 2023.0.1.3 changed the config-load priority mechanism] E --> F[Nacos PropertySource registered in the bootstrap phase is overwritten or discarded by later steps] In short: SCA 2023.0.1.3 made a backward-incompatible internal change, causing the Nacos extension config in bootstrap.yml to be “dropped” in the loading chain, while config in application.yml takes the new path and is unaffected. ...

2025-07-24 10:40:26 PM · 3 min
Backend Troubleshooting

Some Common Nacos Configurations UPDATING

bootstrap config bootstrap.yaml server: port: 10888 tomcat: uri-encoding: UTF-8 spring: profiles: active: dev application: name: archive-service cloud: nacos: config: file-extension: yaml shared-configs: - data-id: shared-spring.yaml refresh: false - data-id: shared-redis.yaml refresh: false - data-id: shared-mybatis.yaml refresh: false - data-id: shared-logs.yaml refresh: false - data-id: shared-feign.yaml refresh: false - data-id: shared-logs.yaml # shared logging config refresh: false # - data-id: shared-feign.yaml # shared feign config # refresh: false lia: jdbc: database: tb_archive bootstrap-dev.yaml spring: cloud: nacos: server-addr: 192.168.2.115:8848 # nacos registry discovery: namespace: f923fb34-cb0a-4c06-8fca-ad61ea61a3f0 group: DEFAULT_GROUP ip: 192.168.2.115 logging: level: asia.liminality: debug nacos config shared-spring.yaml spring: jackson: default-property-inclusion: non_null main: allow-bean-definition-overriding: true mvc: pathmatch: # Fixes the exception: swagger Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException # Because Springfox's path matching is based on AntPathMatcher, while Spring Boot 2.6.X uses PathPatternMatcher matching-strategy: ant_path_matcher shared-redis.yaml spring: redis: host: ${lia.redis.host:192.168.2.115} password: ${lia.redis.password:github} lettuce: pool: max-active: ${lia.redis.pool.max-active:8} max-idle: ${lia.redis.pool.max-idle:8} min-idle: ${lia.redis.pool.min-idle:1} max-wait: ${lia.redis.pool.max-wait:300} shared-mybatis.yaml MySQL spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://${sh.jdbc.host:127.0.0.1}/${sh.jdbc.database}?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false username: ${lia.jdbc.username:root} password: ${lia.jdbc.password:794211} mybatis-plus: configuration: default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler global-config: db-config: logic-delete-field: deletedAt logic-not-delete-value: "null" logic-delete-value: "now()" id-type: assign_id insert-strategy: not_null update-strategy: not_null PostgreSQL spring: datasource: driver-class-name: org.postgresql.Driver url: jdbc:postgresql://${lia.jdbc.host:192.168.2.115}:${lia.jdbc.port:5432}/${lia.jdbc.database}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai username: ${lia.jdbc.username:postgres} password: ${lia.jdbc.password:github} mybatis-plus: configuration: default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler global-config: db-config: logic-delete-field: deletedAt logic-not-delete-value: "null" logic-delete-value: "now()" id-type: assign_id insert-strategy: ignored update-strategy: ignored select-strategy: not_null shared-logs.yaml logging: pattern: dateformat: HH:mm:ss.SSS console: "%clr(%d{${LOG_DATEFORMAT_PATTERN}}){faint}-[${hostname}][%X{requestId:-sys}] %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n" file: "%d{${LOG_DATEFORMAT_PATTERN}}-[${hostname}][%X{requestId:-sys}]-${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%15.15t] %-40.40logger{39} : %m%n" file: path: "logs/${spring.application.name}" shared-feign.yaml feign: client: config: default: # the default global config loggerLevel: BASIC # log level; BASIC is the basic request and response info httpclient: enabled: true # enable Feign's HttpClient support max-connections: 200 # max connections max-connections-per-route: 50 # max connections per route sentinel: enabled: true

2025-06-24 07:01:02 PM · 2 min

How to Download Wistia Videos

Automatic tool (unmaintained) Paste the video link you got by right-clicking the Wistia player here: Parse video Manual method Right-click on the playing video Choose “Copy link.” Find the video ID in the link You’ll see something like: wvideo=tra6gsm6rl Here tra6gsm6rl is the video’s ID. If it’s not in the link, you can also: Open the page source (view source) and search for: hashedId=tra6gsm6rl Open the embed page Visit in your browser: ...

2025-05-23 08:26:17 PM · 1 min

A Guide to Suffering Through WeChat Mini Program Development UPDATING

The auth interaction flow between a WeChat Mini Program and the backend The core flow of WeChat Mini Program login: the mini program gets a temporary credential code → the backend exchanges the code for an openid → the backend issues its own JWT to the mini program. Below is the overall flow diagram. sequenceDiagram participant MP as WeChat Mini Program participant WX as WeChat server participant GW as gateway participant Auth as auth module participant User as user-service participant DB as lia_user database MP->>WX: wx.login() to get code WX-->>MP: returns temporary code MP->>GW: POST /auth/wx-login {code} GW->>Auth: forward the request Auth->>WX: code2session(code) WX-->>Auth: returns openid + session_key Auth->>User: OpenFeign to query/create the user User->>DB: query by openid DB-->>User: user record (or empty) User-->>Auth: return userId Auth->>Auth: generate JWT Auth-->>MP: return token + user info MP->>MP: store into Storage Breaking down the key points 1. The mini program gets the code (frontend) ...

2025-04-23 02:02:30 PM · 2 min
WeChat Mini Program Dev Frontend Development

Message Middleware: Interview Questions UPDATING

RabbitMQ How does RabbitMQ guarantee messages are not lost? Where can loss happen publisher -> exchange Producer confirmation mechanism ack publish-confirm nack publish-confirm ack publish-return How to handle a failed message Callback method Log it Save to the database and resend on a schedule exchange -> queue Message persistence Exchange persistence Queue persistence Message persistence queue queue -> consumer Consumer acknowledgment mechanism How is the duplicate-consumption problem in RabbitMQ solved? Causes of duplicate consumption ...

2025-03-23 01:21:54 PM · 1 min
Interview Questions