DEV Community

Zaw Htut Win
Zaw Htut Win

Posted on

Full Stack Development with Spring Boot (အခန်း ၇)

JWT နဲ့ Spring Security

စစချင်း pom.xml ထဲမှာ ဒီ dependency ကို ထည့်ပါ။

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

ပြီးရင်တော့ JwtAuthenticationFilter ကို ဟောသလို ရေးပြီး filter package ထဲထည့်ထားပါ။ JWT အကြောင်းကို နောက် chapter မှာ ရှင်းပြပါ့မယ်။

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final String SECRET = "my-secret-key";

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {

        String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);

        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return;
        }

        try {
            String token = authHeader.substring(7);
            Claims claims = Jwts.parser()
                                .setSigningKey(SECRET)
                                .parseClaimsJws(token)
                                .getBody();

            // Set authentication if needed
            String username = claims.getSubject();
            UsernamePasswordAuthenticationToken authentication =
                new UsernamePasswordAuthenticationToken(username, null, List.of());

            SecurityContextHolder.getContext().setAuthentication(authentication);
        } catch (Exception e) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return;
        }

        filterChain.doFilter(request, response);
    }
}

Enter fullscreen mode Exit fullscreen mode

ဒါကတော့ Security Config ဖြစ်ပါတယ်။ ဒါကို base package မှာပဲထည့်ပါ။

package com.clinic.app;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

import com.clinic.app.filters.ApiKeyAuthFilter;

@Configuration
public class SecurityConfig {

     @Autowired 
     JwtAuthenticationFilter apiKeyAuthFilter;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth").permitAll()
                .anyRequest().authenticated()
            )
            .addFilterBefore(apiKeyAuthFilter, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

}
Enter fullscreen mode Exit fullscreen mode

ဒါကတော့ Bearer token ထုတ်ပေးမယ့် api/auth endpoint ပါ။ localhost:8080/api/auth ကို user name နဲ့ password နဲ့ ပေးလိုက်ရင် ထွက်လာမှာပါ။ ဒီနေရာမှာ username နဲ့ password ကို လွယ်ကူအောင် အသေရေးထားပါတယ်။ SECRET လည်း ထို့အတူပါပဲ။

@RestController
@RequestMapping("/api")
public class AuthController {

    private final String SECRET = "my-secret-key";

    @PostMapping("/auth")
    public ResponseEntity<?> authenticate(@RequestBody AuthRequest request) {
        // In real apps, you would validate username/password
        if ("user".equals(request.getUsername()) && "pass".equals(request.getPassword())) {
            String token = Jwts.builder()
                    .setSubject(request.getUsername())
                    .signWith(SignatureAlgorithm.HS256, SECRET)
                    .compact();

            return ResponseEntity.ok(Collections.singletonMap("token", token));
        } else {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

ပြီးရင် postman က နေ http://localhost:8080/api/auth ကို username,password ပေးပြီး POST request ခေါ်ကြည့်ပါ။ Bearer Token ရလာပါလိမ့်မယ်။

                .requestMatchers("/api/auth").permitAll()
                .anyRequest().authenticated()
Enter fullscreen mode Exit fullscreen mode

ဒီ code ထဲမှာ ပါတဲ့ အတိုင်း /api/auth က လွဲရင် ကျန်တဲ့ route တွေအကုန်လုံးကို Spring Security က စစ်ပါတယ်။ Authenticate လုပ်ပါတယ်။ Bearer Token ပါမှ ပေးဝင်တဲ့ သဘောပါ။

JWT အကြောင်း နဲနဲပြောပြပါ့မယ်။ JWT မှာ Header, Payload နဲ့ Signature ဆိုပြီး သုံးမျိုးပါပါတယ်။

ဒါက Header ပါ

{
  "alg": "HS256",
  "typ": "JWT"
}

Enter fullscreen mode Exit fullscreen mode

ဒါက payload

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true,
  "exp": 1715467200
}
Enter fullscreen mode Exit fullscreen mode

ဒါက Payload ကို Java မှာ create လုပ်တဲ့နည်း က ခုနက example ထဲ မှာ ပါပပြီးသားပါ။ username နဲ့ password မှန်ရင် jwt ကို generate လုပ်ပြီး token ထုတ်ပေးပါတယ်။

            String token = Jwts.builder()
                    .setSubject(request.getUsername())
                    .signWith(SignatureAlgorithm.HS256, SECRET)
                    .compact();
Enter fullscreen mode Exit fullscreen mode

ဒီတော့ JWT ရဲ့ Format က

<base64url-encoded-header>.<base64url-encoded-payload>.<base64url-encoded-signature>
Enter fullscreen mode Exit fullscreen mode

နောက်ဆုံးက signature ကို token ကို ဘယ်သူမှ ပြင်ပြီးမသုံးဖူးဆိုတာ အာမခံတဲ့ အနေနဲ့ သုံးပါတယ်။ သူ့ရဲ့အလုပ်လုပ်ပုံက header နဲ့ payload ကို encode လုပ်ပြီး secret key နဲ့ sign လုပ်ထားတာပါ။ example ဒါမျိုးပါ။


eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
Enter fullscreen mode Exit fullscreen mode

Top comments (0)