Information Security 12 min read

Technical Selection and Implementation of Authentication: JWT vs Session

This article compares JWT and session-based authentication, detailing their differences, certification processes, advantages, disadvantages, security considerations, performance impacts, token renewal, and revocation strategies, and provides a complete Java implementation using Spring, Redis, and custom utility classes.

Architect
Architect
Architect
Technical Selection and Implementation of Authentication: JWT vs Session

In recent work on a user‑management module, the author needed to implement authentication and chose to compare JWT and session‑based approaches.

Technical Selection

Difference

Session stores user state on the server, while JWT stores it on the client.

Authentication Process

Session‑based flow

User logs in, server creates a session and stores it in the database.

Server returns a sessionId in a cookie.

Subsequent requests include the cookie; server validates the session by looking up the sessionId .

JWT‑based flow

User logs in, server creates a token and stores it in the database.

Frontend stores the token in a cookie or local storage and sends it in request headers.

Server validates the token by checking its presence and validity in the database.

Pros and Cons

JWT is stateless and works well in distributed environments without extra session sharing, but it cannot be used on mobile browsers that block cookies; session requires server‑side storage and cookie support.

Security

JWT payload is only Base64‑encoded, so sensitive data should not be stored inside; session data remains on the server, offering better protection.

Performance

JWTs can become large, increasing HTTP header size, whereas a session ID is a short string, making session‑based requests lighter.

One‑time Use

JWTs are immutable; to modify contents a new token must be issued.

Revocation

Once issued, a JWT remains valid until expiration; revocation can be handled by tracking tokens in Redis.

Renewal

To extend JWT validity, either issue a new token on each request (inefficient) or refresh its Redis expiration.

Choice

The author prefers JWT for its simplicity in distributed systems, supplementing it with Redis to handle revocation and renewal.

Implementation

Dependencies

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.3</version>
</dependency>

JWT Utility Class

public class JWTUtil {
    private static final Logger logger = LoggerFactory.getLogger(JWTUtil.class);
    private static final String TOKEN_SECRET = "123456";
    public static String generateToken(UserTokenDTO userTokenDTO) { ... }
    public static UserTokenDTO parseToken(String token) { ... }
}

Redis Service Wrapper

public final class RedisServiceImpl implements RedisService {
    private final Long DURATION = 1 * 24 * 60 * 60 * 1000L;
    @Resource private RedisTemplate redisTemplate;
    @PostConstruct public void init() { ... }
    public void set(String key, String value) { ... }
    public String get(String key) { ... }
    public boolean delete(String key) { ... }
    public Long getExpireTime(String key) { ... }
}

Business Logic

Login

public String login(LoginUserVO loginUserVO) {
    // validate credentials
    // generate token
    // store token in Redis
    return token;
}

Logout

public boolean loginOut(String id) {
    boolean result = redisService.delete(id);
    if (!result) { throw new UserException(ErrorCodeEnum.TNP1001003); }
    return result;
}

Update Password

public String updatePassword(UpdatePasswordUserVO vo) {
    // update password in DB
    // generate new token
    // replace token in Redis
    return token;
}

Interceptor for Token Validation and Renewal

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    String authToken = request.getHeader("Authorization");
    String token = authToken.substring("Bearer".length() + 1).trim();
    UserTokenDTO dto = JWTUtil.parseToken(token);
    if (redisService.get(dto.getId()) == null || !redisService.get(dto.getId()).equals(token)) {
        return false;
    }
    if (redisService.getExpireTime(dto.getId()) < 30 * 60) {
        redisService.set(dto.getId(), token);
    }
    return true;
}

Interceptor Configuration

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticateInterceptor())
                .excludePathPatterns("/logout/**", "/login/**")
                .addPathPatterns("/**");
    }
    @Bean
    public AuthenticateInterceptor authenticateInterceptor() {
        return new AuthenticateInterceptor();
    }
}

The article concludes with a note that admin users have extra permissions and that passwords should be transmitted encrypted in production.

JavaRedisSpringSecurityAuthenticationJWTsession
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.