স্প্রিং সিকিউরিটি হল একটি শক্তিশালী নিরাপত্তা ফ্রেমওয়ার্ক যা আপনার অ্যাপ্লিকেশনগুলিতে অথেনটিকেশন এবং অথরাইজেশন নিশ্চিত করার জন্য ব্যবহৃত হয়। API Security নিশ্চিত করতে স্প্রিং সিকিউরিটি ব্যবহারের মাধ্যমে আপনি RESTful APIs, JWT (JSON Web Tokens), OAuth2 বা Basic Authentication ব্যবহার করে নিরাপদ ও সুরক্ষিত API তৈরি করতে পারেন।
স্প্রিং সিকিউরিটি এবং API সিকিউরিটি
API সিকিউরিটি গুরুত্বপূর্ণ কারণ এটি আপনার API এর অ্যাক্সেস নিয়ন্ত্রণ করে এবং অবৈধ অনুরোধগুলো ব্লক করে। API সিকিউরিটির বিভিন্ন পদ্ধতি রয়েছে যেমন:
- JWT Authentication
- OAuth2 Authentication
- Basic Authentication
- API Rate Limiting
- CORS Configuration
এখানে, Spring Security ব্যবহার করে একটি REST API সিকিউর করতে বিভিন্ন পদ্ধতির উদাহরণ দেওয়া হবে।
১. Spring Security ও JWT Authentication
JWT Authentication একটি জনপ্রিয় অথেনটিকেশন পদ্ধতি যা স্টেটলেস অথেনটিকেশন সরবরাহ করে। যখন ক্লায়েন্ট লগইন করে, তখন একটি JWT token জেনারেট করা হয়, যা পরবর্তী রিকোয়েস্টে প্রেরিত হয়। এই টোকেনের মাধ্যমে ইউজারের পরিচয় যাচাই করা হয়, এবং API নিরাপদ থাকে।
Step 1: Maven Dependencies
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Security Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- JWT Library -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.11.5</version>
</dependency>
<!-- Spring Boot Starter for JPA (for persistence if needed) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
Step 2: JWT Token Utility
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JwtTokenUtil {
private String secretKey = "yourSecretKey"; // Secret key for signing JWT
private long expirationTime = 1000 * 60 * 60; // 1 hour for access token
// Generate JWT Token
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expirationTime))
.signWith(SignatureAlgorithm.HS512, secretKey)
.compact();
}
// Parse Claims
public Claims getClaims(String token) {
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody();
}
// Validate Token
public boolean validateToken(String token, String username) {
return (username.equals(getUsernameFromToken(token)) && !isTokenExpired(token));
}
// Check if token expired
public boolean isTokenExpired(String token) {
return getClaims(token).getExpiration().before(new Date());
}
// Extract Username from Token
public String getUsernameFromToken(String token) {
return getClaims(token).getSubject();
}
}
Step 3: Authentication Filter
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final JwtTokenUtil jwtTokenUtil;
private final AuthenticationManager authenticationManager;
public JwtAuthenticationFilter(AuthenticationManager authenticationManager, JwtTokenUtil jwtTokenUtil) {
this.authenticationManager = authenticationManager;
this.jwtTokenUtil = jwtTokenUtil;
}
@Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7); // Extract JWT token
String username = jwtTokenUtil.getUsernameFromToken(token);
if (username != null && jwtTokenUtil.validateToken(token, username)) {
// Set authentication context
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(username, null, null)); // Add authorities if needed
}
}
chain.doFilter(request, response);
}
}
Step 4: Spring Security Configuration
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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final JwtTokenUtil jwtTokenUtil;
public SecurityConfig(JwtTokenUtil jwtTokenUtil) {
this.jwtTokenUtil = jwtTokenUtil;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/auth/login", "/auth/refresh").permitAll() // Public endpoints
.anyRequest().authenticated() // Authenticated endpoints
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager(), jwtTokenUtil)); // Add the filter
}
}
Step 5: Controller for Login and Refresh
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AuthenticationController {
private final JwtTokenUtil jwtTokenUtil;
public AuthenticationController(JwtTokenUtil jwtTokenUtil) {
this.jwtTokenUtil = jwtTokenUtil;
}
@PostMapping("/auth/login")
public String login(@RequestBody LoginRequest loginRequest) {
// Authenticate the user (In a real-world app, validate username and password)
String token = jwtTokenUtil.generateToken(loginRequest.getUsername());
return "Bearer " + token; // Return JWT token
}
@PostMapping("/auth/refresh")
public String refresh(@RequestBody RefreshRequest refreshRequest) {
// Validate refresh token and generate new access token
String username = jwtTokenUtil.getUsernameFromToken(refreshRequest.getRefreshToken());
String token = jwtTokenUtil.generateToken(username);
return "Bearer " + token;
}
}
Step 6: DTOs for Login and Refresh
LoginRequest DTO:
public class LoginRequest {
private String username;
private String password;
// Getters and Setters
}
RefreshRequest DTO:
public class RefreshRequest {
private String refreshToken;
// Getter and Setter
}
২. Spring Security API Security Best Practices
Role-Based Authorization (RBAC):
- RBAC হচ্ছে ব্যবহারকারীদের নির্দিষ্ট রোল দিয়ে এক্সেস নিয়ন্ত্রণ। স্প্রিং সিকিউরিটি এই কনসেপ্ট ব্যবহার করে নিরাপত্তা নীতি নির্ধারণ করতে সক্ষম।
উদাহরণ:
http.authorizeRequests() .antMatchers("/admin/**").hasRole("ADMIN") // Only accessible by Admin .antMatchers("/user/**").hasRole("USER") // Only accessible by User .anyRequest().authenticated(); // All other endpoints require authentication- OAuth2 Authentication:
- OAuth2 একটি জনপ্রিয় অথেনটিকেশন এবং অথরাইজেশন স্ট্যান্ডার্ড, যা তৃতীয় পক্ষের (যেমন, Google, Facebook) দ্বারা ব্যবহারকারীর তথ্য যাচাই করতে সাহায্য করে।
- Basic Authentication:
- Basic Authentication সাধারণত API-তে ইউজারের নাম এবং পাসওয়ার্ড ব্যবহার করে অথেনটিকেশন সম্পন্ন করতে ব্যবহৃত হয়।
- Rate Limiting:
- আপনার API-এর উপর Rate Limiting প্রয়োগ করতে পারেন যাতে অধিক অনুরোধগুলোকে সীমাবদ্ধ করা যায়।
- CORS Configuration:
- স্প্রিং সিকিউরিটি ব্যবহার করে আপনি CORS (Cross-Origin Resource Sharing) কনফিগার করতে পারেন, যা API-এর নিরাপত্তা নিশ্চিত করে যখন বিভিন্ন উৎস থেকে API কল করা হয়।
উপসংহার
স্প্রিং সিকিউরিটি ব্যবহার করে API Security বাস্তবায়ন করা একটি শক্তিশালী উপায়, যা JWT Authentication, OAuth2, এবং Basic Authentication এর মতো পদ্ধতির মাধ্যমে আপনার API কে সুরক্ষিত করে। এতে role-based authorization, CORS configuration, rate limiting এবং OAuth2 authentication এর মতো সুবিধা রয়েছে যা API সিকিউরিটির জন্য গুরুত্বপূর্ণ।
Spring Security ব্যবহার করে REST API এর জন্য নিরাপত্তা কনফিগারেশন করা খুবই গুরুত্বপূর্ণ, কারণ এটি API এর মাধ্যমে প্রবাহিত ডেটা এবং রিসোর্সকে সুরক্ষিত রাখে। REST API এর জন্য Spring Security কনফিগারেশনের মাধ্যমে আপনি Authentication এবং Authorization নিয়ন্ত্রণ করতে পারেন।
এখানে একটি REST API এর জন্য Spring Security কনফিগারেশন করার ধাপ দেওয়া হলো:
Step 1: Maven Dependency
প্রথমে, আপনার pom.xml ফাইলে Spring Security এবং Spring Boot Web এর নির্ভরতাগুলি যোগ করুন:
<dependencies>
<!-- Spring Boot Starter Web (for REST API) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Starter Security (for Security) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Spring Boot Starter Data JPA (for Database) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- H2 Database for testing purposes (replace with actual DB in production) -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Spring Boot Starter Validation (for validating inputs) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>
Step 2: Security Configuration for REST API
Spring Security তে REST API-এর জন্য নিরাপত্তা কনফিগার করার জন্য SecurityConfig ক্লাস তৈরি করুন। এখানে আমরা JWT (JSON Web Token) ব্যবহার করে নিরাপত্তা কনফিগার করব।
SecurityConfig ক্লাস:
package com.example.config;
import com.example.security.JwtAuthenticationFilter;
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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.cors().and() // Enable CORS (Cross-Origin Resource Sharing)
.csrf().disable() // Disable CSRF for stateless REST API
.authorizeRequests()
.antMatchers("/public/**").permitAll() // Public API endpoints
.antMatchers("/api/auth/**").permitAll() // Authentication related endpoints
.anyRequest().authenticated() // All other requests require authentication
.and()
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); // Add JWT authentication filter
return http.build();
}
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
}
Explanation:
cors(): CORS (Cross-Origin Resource Sharing) সক্রিয় করা হয়েছে, কারণ আপনি যদি API থেকে অন্য ডোমেনের অ্যাপ্লিকেশন অ্যাক্সেস করতে চান তবে CORS প্রয়োজন হবে।csrf().disable(): REST API-তে CSRF (Cross-Site Request Forgery) সুরক্ষা সাধারণত প্রয়োজন হয় না, কারণ এটি স্টেটলেস (stateless) অ্যাপ্লিকেশন। ক্লায়েন্ট প্রতিবার নতুন JWT পাঠাবে, তাই CSRF থেকে নিরাপদ থাকবে।authorizeRequests(): এখানে আপনি API এর বিভিন্ন এন্ডপয়েন্টকে বিভিন্নভাবে অ্যাক্সেস কন্ট্রোল করতে পারেন।/public/**: পাবলিক রিসোর্স যা সকল ব্যবহারকারীর জন্য খোলা।/api/auth/**: লগইন বা রেজিস্ট্রেশন এর মতো অথেনটিকেশন সম্পর্কিত এন্ডপয়েন্ট।anyRequest().authenticated(): অন্যান্য সমস্ত রিকোয়েস্ট শুধুমাত্র অথেনটিকেটেড ইউজারদের জন্য।
addFilterBefore(): এখানে কাস্টম JWT ফিল্টার যোগ করা হয়েছে, যা UsernamePasswordAuthenticationFilter এর আগে রিকোয়েস্টে চলবে। এটি JWT টোকেন যাচাই করবে এবং ইউজারকে অথেনটিকেট করবে।
Step 3: JWT Authentication Filter
JWT টোকেন যাচাই করার জন্য একটি কাস্টম ফিল্টার তৈরি করুন। এই ফিল্টারটি HTTP হেডার থেকে JWT টোকেন পড়ে এবং তার ভিত্তিতে ব্যবহারকারীকে অথেনটিকেট করবে।
JwtAuthenticationFilter ক্লাস:
package com.example.security;
import com.example.util.JwtUtil;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
public JwtAuthenticationFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String token = getJwtFromRequest(request);
if (token != null && jwtUtil.validateToken(token)) {
String username = jwtUtil.getUsernameFromToken(token);
if (username != null) {
// Create authentication token and set it in the security context
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(username, null, null)
);
}
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7); // Remove "Bearer " prefix
}
return null;
}
}
Explanation:
getJwtFromRequest(request): HTTP রিকোয়েস্ট থেকে Authorization হেডার থেকে JWT টোকেন বের করা হয়।jwtUtil.validateToken(token): JWT টোকেন যাচাই করা হয়।SecurityContextHolder.getContext().setAuthentication(): যদি JWT টোকেন বৈধ হয়, তবেSecurityContextএ ব্যবহারকারীর তথ্য সেট করা হয়, যাতে Spring Security পরবর্তী রিকোয়েস্টগুলিতে এটি ব্যবহার করতে পারে।
Step 4: JWT Utility Class
এটি একটি JwtUtil ক্লাস যা JWT টোকেন তৈরি এবং যাচাই করার কাজ করবে।
JwtUtil ক্লাস:
package com.example.util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JwtUtil {
private final String SECRET_KEY = "mySecretKey";
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10 hours expiration
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public boolean validateToken(String token) {
try {
Claims claims = extractClaims(token);
return !claims.getExpiration().before(new Date());
} catch (Exception e) {
return false;
}
}
public String getUsernameFromToken(String token) {
return extractClaims(token).getSubject();
}
private Claims extractClaims(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
}
Explanation:
generateToken(username): একটি নতুন JWT টোকেন তৈরি করে।validateToken(token): JWT টোকেনটি বৈধ কিনা তা যাচাই করে।getUsernameFromToken(token): JWT টোকেন থেকে ব্যবহারকারীর নাম বের করে।
Step 5: REST Controller
এখন, আমরা একটি REST Controller তৈরি করব যেখানে ব্যবহারকারী লগইন করবে এবং JWT টোকেন পাবে।
AuthController ক্লাস:
package com.example.controller;
import com.example.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private JwtUtil jwtUtil;
@PostMapping("/login")
public String login(@RequestParam String username, @RequestParam String password) {
// Authenticate the user (in a real-world scenario, authenticate with DB)
if ("user".equals(username) && "password123".equals(password)) {
return jwtUtil.generateToken(username); // Generate and return JWT
} else {
throw new RuntimeException("Invalid credentials");
}
}
}
Explanation:
/api/auth/login: এই এন্ডপয়েন্টে ব্যবহারকারী ইউজারনেম এবং পাসওয়ার্ড প্রদান করবে এবং যদি সঠিক হয় তবে JWT টোকেন প্রদান করা হবে।
Conclusion
এটি একটি পূর্ণাঙ্গ উদাহরণ ছিল যেখানে Spring Security এবং JWT ব্যবহার করে REST API এর জন্য নিরাপত্তা কনফিগার করা হয়েছে। এই কনফিগারেশনটি আপনাকে stateless অথেনটিকেশন এবং অথরাইজেশন প্রদান করে, যেখানে JWT টোকেন ব্যবহারকারীদের সেশন ট্র্যাকিং ছাড়াই API রিসোর্সে অ্যাক্সেস দেয়।
Token-Based Authentication হল একটি জনপ্রিয় অথেন্টিকেশন মেথড যা সাধারণত ওয়েব অ্যাপ্লিকেশন বা মাইক্রোসার্ভিস আর্কিটেকচারে ব্যবহৃত হয়। এই মেথডে, ব্যবহারকারী তার লগইন তথ্য সফলভাবে প্রদান করার পরে একটি টোকেন পায়, যেটি পরবর্তী রিকোয়েস্টে প্রমাণীকরণের জন্য ব্যবহৃত হয়। এই টোকেনটি সাধারণত JWT (JSON Web Token) বা OAuth ভিত্তিক হতে পারে।
Stateless Session বলতে বোঝায় যে সার্ভার ক্লায়েন্টের সেশন সংরক্ষণ করে না। এর পরিবর্তে, প্রতিটি রিকোয়েস্টে ক্লায়েন্ট তার নিজস্ব অথেন্টিকেশন ইনফরমেশন (টোকেন) সহ রিকোয়েস্ট পাঠায় এবং সার্ভার শুধুমাত্র সেই তথ্য যাচাই করে অ্যাক্সেস অনুমতি দেয়।
Spring Security তে Token-Based Authentication এবং Stateless Session কনফিগার করার জন্য আমরা নিম্নলিখিত স্টেপগুলি অনুসরণ করব।
Step 1: Spring Security Configuration for Stateless Session
1.1: Spring Security Configuration Class তৈরি করুন
Spring Security তে Stateless সেশন কনফিগার করতে, আমরা HttpSecurity কনফিগারেশন এ sessionManagement() ব্যবহার করি এবং STATELESS হিসেবে কনফিগার করি। এর মানে হল যে সার্ভার কোনো সেশন তৈরি করবে না।
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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() // CSRF বন্ধ করা
.authorizeRequests()
.antMatchers("/auth/**").permitAll() // /auth endpoint গুলোর জন্য অ্যাক্সেস খোলা
.anyRequest().authenticated() // অন্য সকল রিকোয়েস্টের জন্য অথেন্টিকেশন প্রয়োজন
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS); // Stateless session
// JWT filter যোগ করা
http.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
sessionCreationPolicy(SessionCreationPolicy.STATELESS): এটি সার্ভারকে বলছে যে কোনও সেশন সংরক্ষণ করা হবে না। সার্ভার প্রতিটি রিকোয়েস্টের সাথে নতুন অথেন্টিকেশন ভ্যালিডেশন করবে এবং সেশন তৈরি করবে না।- JWT Filter: যেহেতু আমরা Token-Based Authentication ব্যবহার করতে যাচ্ছি, আমাদের একটি JWT ফিল্টার প্রয়োজন যা প্রতিটি রিকোয়েস্টের Authorization হেডারে পাঠানো টোকেনটি যাচাই করবে।
Step 2: JWT Authentication Filter
Token-Based Authentication সাধারণত JWT (JSON Web Token) এর মাধ্যমে হয়। JWT টোকেনের সাহায্যে প্রতিটি HTTP রিকোয়েস্ট অথেন্টিকেট করা হয়। এটি সাধারণত একটি Authorization হেডারে পাঠানো হয়, উদাহরণস্বরূপ:
Authorization: Bearer <JWT_TOKEN>
2.1: JWT Authentication Filter
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter(urlPatterns = "/api/*")
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private static final String TOKEN_PREFIX = "Bearer ";
private static final String HEADER_STRING = "Authorization";
private final JwtUtil jwtUtil; // JwtUtil একটি Utility ক্লাস যা টোকেন জেনারেট এবং ভ্যালিডেট করতে সাহায্য করবে
public JwtAuthenticationFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
@Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String header = request.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(request, response);
return;
}
String token = header.replace(TOKEN_PREFIX, "");
String username = jwtUtil.extractUsername(token);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
if (jwtUtil.validateToken(token, username)) {
Authentication authentication = new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
}
}
- Authorization Header থেকে Token Extract করা: ফিল্টারটি HTTP হেডার থেকে
Authorizationটোকেন বের করে, এবং এটি যাচাই করে যদি টোকেনটি বৈধ হয় তবে Authentication কনটেক্সট সেট করে। jwtUtil.extractUsername(token): এই ফাংশনটি টোকেন থেকে ইউজারনেম বের করে।SecurityContextHolder.getContext().setAuthentication(authentication): এটি Spring Security কনটেক্সট এ সঠিক Authentication অবজেক্ট সেট করে।
Step 3: JWT Utility Class
JWT Utility ক্লাসটি টোকেন জেনারেট এবং ভ্যালিডেশন করার জন্য ব্যবহার করা হবে।
3.1: JwtUtil Class
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.Claims;
import java.util.Date;
public class JwtUtil {
private static final String SECRET_KEY = "secretKey"; // নিজের SECRET_KEY ব্যবহার করুন
// টোকেন জেনারেট করা
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60)) // 1 hour
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
// টোকেন থেকে ইউজার নাম বের করা
public String extractUsername(String token) {
return extractClaims(token).getSubject();
}
// টোকেন থেকে তথ্য বের করা
private Claims extractClaims(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
// টোকেন ভ্যালিডেশন
public boolean validateToken(String token, String username) {
return (username.equals(extractUsername(token)) && !isTokenExpired(token));
}
// টোকেনের মেয়াদ শেষ হয়েছে কিনা
private boolean isTokenExpired(String token) {
Date expiration = extractClaims(token).getExpiration();
return expiration.before(new Date());
}
}
generateToken(): এই ফাংশনটি ইউজারনেমের সাথে একটি JWT টোকেন তৈরি করে।extractUsername(): টোকেন থেকে ইউজারনেম বের করার জন্য ব্যবহৃত হয়।validateToken(): টোকেনটি ভ্যালিড কিনা যাচাই করে।isTokenExpired(): টোকেনের মেয়াদ শেষ হয়েছে কিনা যাচাই করে।
Step 4: Token-Based Authentication Flow
- User Login: ব্যবহারকারী তার ইউজারনেম এবং পাসওয়ার্ড দিয়ে লগইন করে।
- Generate JWT Token: লগইন সফল হলে সার্ভার একটি JWT টোকেন জেনারেট করে এবং ক্লায়েন্টকে প্রদান করে।
- Token Validation: ক্লায়েন্ট পরবর্তী রিকোয়েস্টের সাথে JWT টোকেন পাঠায়। সার্ভার
JwtAuthenticationFilterএর মাধ্যমে টোকেন যাচাই করে এবং যদি টোকেনটি বৈধ হয়, তবে ব্যবহারকারীকে অথেন্টিকেট করে।
উপসংহার
Token-Based Authentication এবং Stateless Session Spring Security-তে খুবই গুরুত্বপূর্ণ একটি সিকিউরিটি প্যাটার্ন, যা আধুনিক ওয়েব অ্যাপ্লিকেশন ও মাইক্রোসার্ভিস আর্কিটেকচারে ব্যবহৃত হয়। এখানে, JWT ভিত্তিক অথেন্টিকেশন সেটআপ করতে JwtAuthenticationFilter, JwtUtil, এবং SecurityConfig কনফিগারেশন তৈরি করা হয়েছে। Stateless সেশন ব্যবহারের মাধ্যমে সার্ভার কোন সেশন ম্যানেজমেন্ট না করে, প্রতিটি রিকোয়েস্টের সাথে টোকেন যাচাই করে।
Spring Security হল একটি শক্তিশালী ফ্রেমওয়ার্ক যা Spring ভিত্তিক অ্যাপ্লিকেশনগুলোতে নিরাপত্তা ব্যবস্থা প্রদান করে। RESTful API-তে নিরাপত্তা যোগ করার জন্য Spring Security ব্যবহার করা হয়, যেখানে Authentication, Authorization, CSRF Protection, এবং আরও অনেক নিরাপত্তা কার্যক্রম পরিচালিত হয়।
এখানে Spring Security ব্যবহারের মাধ্যমে REST API-এর জন্য Authentication ও Authorization কনফিগারেশন দেওয়া হয়েছে, যাতে আমরা JWT (JSON Web Token) ব্যবহার করে নিরাপত্তা ব্যবস্থা ইমপ্লিমেন্ট করতে পারি।
Spring Security with JWT Authentication for REST API: উদাহরণ
Step 1: Dependencies
প্রথমে, আপনাকে আপনার pom.xml ফাইলে প্রয়োজনীয় ডিপেন্ডেন্সি যোগ করতে হবে।
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
Step 2: JWT Utility Class
JWT Utility Class তৈরি করা হবে, যা Token Generation এবং Token Validation পরিচালনা করবে।
import io.jsonwebtoken.*;
import java.util.Date;
import org.springframework.stereotype.Component;
@Component
public class JwtUtil {
private String SECRET_KEY = "your-256-bit-secret-key";
private final int EXPIRATION_TIME = 1000 * 60 * 60 * 10; // 10 Hours
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public String extractUsername(String token) {
return Jwts.parserBuilder()
.setSigningKey(SECRET_KEY)
.build()
.parseClaimsJws(token)
.getBody()
.getSubject();
}
public boolean isTokenExpired(String token) {
return getExpirationDateFromToken(token).before(new Date());
}
public Date getExpirationDateFromToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(SECRET_KEY)
.build()
.parseClaimsJws(token)
.getBody()
.getExpiration();
}
public boolean validateToken(String token, String username) {
return (username.equals(extractUsername(token)) && !isTokenExpired(token));
}
}
Step 3: Authentication Filter
JWT Token যাচাই করার জন্য Authentication Filter তৈরি করা হবে। এটি প্রতিটি HTTP রিকোয়েস্টের হেডারে পাঠানো JWT টোকেন যাচাই করবে।
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
String username = null;
String token = null;
if (authHeader != null && authHeader.startsWith("Bearer ")) {
token = authHeader.substring(7);
username = jwtUtil.extractUsername(token);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(token, userDetails.getUsername())) {
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
filterChain.doFilter(request, response);
}
}
Step 4: Spring Security Configuration
Spring Security Configuration তৈরি করা হবে যাতে JWT Filter এবং Custom Authentication Handler যুক্ত করা যায়।
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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests()
.requestMatchers("/auth/**").permitAll() // Allow login without authentication
.anyRequest().authenticated() // Other requests need authentication
.and()
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
Explanation:
csrf().disable(): REST API-তে CSRF সাধারণত নিষ্ক্রিয় করা হয়, কারণ API কল স্টেটলেস।addFilterBefore: CustomJwtAuthenticationFilterSpring Security filter chain-এ যুক্ত করা হয়েছে, যাতে টোকেন যাচাই করা হয়।
Step 5: Authentication Controller
লগইন করতে এবং JWT Token পেতে একটি API তৈরি করা হবে।
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtUtil jwtUtil;
@PostMapping("/login")
public String login(@RequestBody AuthRequest authRequest) {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
);
return jwtUtil.generateToken(authRequest.getUsername());
}
}
class AuthRequest {
private String username;
private String password;
// Getters and Setters
}
Explanation:
loginMethod: ব্যবহারকারী যখন লগইন করবে, তখন JWT Token ইস্যু করা হবে।
Step 6: Accessing Protected Resources
আপনি নিরাপদ GET বা POST রিকোয়েস্ট করার জন্য JWT Token ব্যবহার করবেন। নিচে একটি উদাহরণ দেওয়া হয়েছে:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ApiController {
@GetMapping("/protected")
public String getProtectedData() {
return "This is a protected resource!";
}
}
এখানে /protected এন্ডপয়েন্টটি JWT Token যাচাই করবে এবং Token বৈধ হলে শুধুমাত্র এক্সেস পাওয়া যাবে।
Step 7: Testing with Postman
- প্রথমে
/auth/loginরিকোয়েস্ট দিয়ে JWT Token পেতে হবে। - তারপর এই JWT Token হেডারে পাঠিয়ে
/protectedএন্ডপয়েন্টে GET রিকোয়েস্ট পাঠাতে হবে।
Example:
Authorization Header:
Bearer <JWT_TOKEN>
Conclusion
এটি একটি সিম্পল JWT Authentication পদ্ধতি যেখানে Spring Security ব্যবহৃত হয়েছে REST API সুরক্ষিত করার জন্য। মূল পদ্ধতিগুলি ছিল:
- JWT Token Generation - লগইন করার সময় টোকেন তৈরি করা।
- JWT Token Validation - প্রতিটি রিকোয়েস্টের সাথে টোকেন যাচাই করা।
- Spring Security Configuration - টোকেন যাচাই করতে Spring Security ফিল্টার ব্যবহার করা।
এটি নিরাপদ REST API নির্মাণের জন্য একটি কার্যকরী পদ্ধতি।
Read more