Spring Security এবং JWT (JSON Web Token)

Java Technologies - স্প্রিং সিকিউরিটি (Spring Security)
145
145

Spring Security এবং JWT (JSON Web Token) একসঙ্গে ব্যবহৃত হলে একটি শক্তিশালী অথেনটিকেশন এবং অথরাইজেশন সিস্টেম তৈরি করা যায়। JWT সাধারণত stateless authentication এর জন্য ব্যবহৃত হয়, যেখানে সার্ভার কোনও স্টেট (যেমন, সেশন) ট্র্যাক করে না। এই কারণে, JWT একটি RESTful API বা মাইক্রোসার্ভিস অ্যাপ্লিকেশন তৈরির জন্য আদর্শ।

JWT ব্যবহার করার মাধ্যমে, আপনি একটি টোকেন তৈরি করতে পারেন যা ব্যবহারকারীর তথ্য (যেমন: ব্যবহারকারীর নাম, রোল, ইত্যাদি) ধারণ করে এবং এটি সার্ভারকে জানাতে সক্ষম হয় যে ব্যবহারকারী বৈধ।


Spring Security এবং JWT ইন্টিগ্রেশন

Spring Security ব্যবহার করে JWT টোকেন বেসড অথেনটিকেশন সেটআপ করার জন্য নিচে প্রক্রিয়া দেওয়া হল।

1. Maven Dependency

প্রথমে, আপনার pom.xml ফাইলে Spring Security এবং JWT লাইব্রেরি যুক্ত করুন:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.11.5</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.11.5</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.11.5</version>
        <scope>runtime</scope>
    </dependency>
</dependencies>

2. JWT Utility Class

JWT টোকেন তৈরি এবং যাচাই করার জন্য একটি Utility ক্লাস তৈরি করুন:

package com.example.security;

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";  // Secret key for signing the JWT

    // Generate JWT Token
    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))  // Valid for 10 hours
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }

    // Extract username from token
    public String extractUsername(String token) {
        return extractClaims(token).getSubject();
    }

    // Extract claims (like expiration date, etc.)
    public Claims extractClaims(String token) {
        return Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody();
    }

    // Check if token is expired
    private boolean isTokenExpired(String token) {
        return extractClaims(token).getExpiration().before(new Date());
    }

    // Validate the token
    public boolean validateToken(String token, String username) {
        return (username.equals(extractUsername(token)) && !isTokenExpired(token));
    }
}

3. Custom JWT Authentication Filter

JWT টোকেন যাচাই করার জন্য একটি filter তৈরি করুন:

package com.example.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class JwtRequestFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) 
        throws javax.servlet.ServletException, java.io.IOException {

        final String authorizationHeader = request.getHeader("Authorization");

        String username = null;
        String jwt = null;

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            jwt = authorizationHeader.substring(7); // Get the token from "Bearer <token>"
            username = jwtUtil.extractUsername(jwt);
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            if (jwtUtil.validateToken(jwt, username)) {
                UsernamePasswordAuthenticationToken authenticationToken =
                        new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }

        chain.doFilter(request, response);
    }
}

4. Spring Security Configuration

Spring Security কনফিগারেশন ক্লাস তৈরি করুন যেখানে JWT Request Filter এবং অন্যান্য নিরাপত্তা কনফিগারেশন থাকবে:

package com.example.security;

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.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()  // Disable CSRF for simplicity
            .authorizeRequests()
                .antMatchers("/authenticate").permitAll()  // Permit authenticate endpoint for everyone
                .anyRequest().authenticated()  // All other requests need authentication
            .and()
            .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);  // Add JWT filter before the standard UsernamePasswordAuthenticationFilter
    }
}

5. Authentication Controller

এখন একটি কন্ট্রোলার তৈরি করুন যেখানে ব্যবহারকারী JWT টোকেন পাবেন:

package com.example.controller;

import com.example.security.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
public class AuthenticationController {

    @Autowired
    private JwtUtil jwtUtil;

    @PostMapping("/authenticate")
    public String createToken(@RequestParam String username) {
        // In a real-world scenario, authenticate the user with username and password
        return jwtUtil.generateToken(username);  // Generate JWT token
    }
}

6. Testing the JWT Authentication

  • প্রথমে /authenticate এ ব্যবহারকারীর নাম পাঠিয়ে একটি JWT টোকেন পেতে হবে।
  • তারপর এই টোকেনটি Authorization হেডারে "Bearer " সহ সার্ভারে পাঠাতে হবে।

Example Request:

POST /authenticate?username=johnDoe HTTP/1.1
Host: localhost:8080

Example Response:

{
  "token": "<JWT_TOKEN>"
}

এবং পরবর্তী রিকোয়েস্টে JWT টোকেন পাঠান:

GET /secure-endpoint HTTP/1.1
Host: localhost:8080
Authorization: Bearer <JWT_TOKEN>

Conclusion

Spring Security এবং JWT ব্যবহার করে আপনি একটি নিরাপদ এবং স্ট্যাটলেস অথেনটিকেশন সিস্টেম তৈরি করতে পারেন। JWT বেসড অথেনটিকেশন RESTful অ্যাপ্লিকেশন এবং মাইক্রোসার্ভিস আর্কিটেকচারে খুবই জনপ্রিয়, কারণ এটি সেশন ট্র্যাকিং ছাড়াই ব্যবহারকারীর পরিচয় যাচাই করতে সহায়ক।

Content added By

JWT কি এবং কেন এটি ব্যবহার করা হয়?

97
97

JWT (JSON Web Token):

JSON Web Token (JWT) একটি কমপ্যাক্ট এবং সুরক্ষিত টোকেন ফরম্যাট যা JSON-ভিত্তিক ডেটা এনকোড করে একটি সাইনড স্ট্রিং আকারে আদান-প্রদান করা হয়। এটি সাধারণত অ্যাপ্লিকেশনগুলোর Authentication এবং Authorization এর জন্য ব্যবহৃত হয়।


JWT কেন ব্যবহৃত হয়?

JWT প্রধানত নিম্নলিখিত কারণগুলোতে ব্যবহৃত হয়:

  1. Stateless Authentication:
    • JWT ব্যবহার করে সার্ভারকে ক্লায়েন্টের সেশন সংরক্ষণ করতে হয় না।
    • টোকেনটি নিজেই সমস্ত তথ্য বহন করে যা সেশন পরিচালনার জন্য প্রয়োজন।
  2. Self-Contained Token:
    • JWT একটি Self-Contained Token যা ব্যবহারকারী সম্পর্কিত সমস্ত তথ্য এনকোড করা থাকে।
  3. Cross-Domain Communication:
    • এটি ওয়েব ব্রাউজার এবং API-এর মধ্যে সহজে কাজ করে।
    • Cross-Origin Resource Sharing (CORS) সমস্যার সমাধানে সাহায্য করে।
  4. Compact and Efficient:
    • JSON ভিত্তিক ডেটা সংরক্ষণ করে যা Human-Readable এবং Lightweight।
    • HTTP Header, Query Parameter, বা Cookie এর মাধ্যমে সহজেই পাঠানো যায়।
  5. Security:
    • টোকেনটি ডিজিটালি সাইনড থাকে (HMAC বা RSA), যা নিশ্চিত করে যে এটি পরিবর্তিত হয়নি।
    • JWT এনক্রিপশন ব্যবহার করলে ডেটা এনক্রিপ্টেড ফরম্যাটে থাকে।

JWT এর স্ট্রাকচার:

JWT তিনটি অংশে বিভক্ত থাকে এবং প্রতিটি অংশ ডট (.) দিয়ে পৃথক করা হয়:

HEADER.PAYLOAD.SIGNATURE
  1. Header:
    • Header-এ টোকেনের প্রকার এবং সাইনিং অ্যালগরিদমের তথ্য থাকে।
    • উদাহরণ:

      {
        "alg": "HS256",
        "typ": "JWT"
      }
      
  2. Payload:
    • Payload-এ টোকেনের মূল তথ্য (Claims) থাকে।
    • উদাহরণ:

      {
        "sub": "1234567890",
        "name": "John Doe",
        "role": "ADMIN",
        "exp": 1688000000
      }
      
  3. Signature:
    • Signature টোকেনের স্বাক্ষর যা হেডার, পে-লোড এবং সিক্রেট কী ব্যবহার করে জেনারেট করা হয়।
    • উদাহরণ:

      HMACSHA256(
        base64UrlEncode(header) + "." + base64UrlEncode(payload),
        secretKey
      )
      

JWT এর কাজ করার ধাপ:

  1. User Authentication:
    • ব্যবহারকারী লগইন করলে সার্ভার তার পরিচয় যাচাই করে।
    • যাচাই সফল হলে, সার্ভার একটি JWT জেনারেট করে এবং ক্লায়েন্টকে প্রদান করে।
  2. Client Stores Token:
    • ক্লায়েন্ট এই JWT টোকেনটি স্থানীয় স্টোরেজ বা কুকিতে সংরক্ষণ করে।
  3. Token Usage:
    • ক্লায়েন্ট পরবর্তী API রিকোয়েস্টের সাথে Authorization হেডারে JWT পাঠায়:

      Authorization: Bearer <JWT_TOKEN>
      
  4. Server Validates Token:
    • সার্ভার প্রাপ্ত টোকেনটি যাচাই করে।
    • যদি টোকেন বৈধ হয়, তাহলে ক্লায়েন্টকে রিসোর্স অ্যাক্সেস করতে দেয়।

JWT এর সুবিধা:

  1. Stateless:
    • সার্ভার সেশনের জন্য কোনো স্টোরেজ বা ডাটাবেসের প্রয়োজন নেই।
  2. Performance:
    • সার্ভারের লোড কমিয়ে দ্রুত অ্যাক্সেস নিশ্চিত করে।
  3. Scalability:
    • ক্লাস্টার বা ডিস্ট্রিবিউটেড সিস্টেমে কাজ করতে সহজ।
  4. Cross-Platform Compatibility:
    • ভিন্ন ভিন্ন টেকনোলজির মধ্যে কাজ করার জন্য উপযুক্ত।

JWT এর সীমাবদ্ধতা:

  1. Token Revocation:
    • JWT স্টেটলেস হওয়ায় এটি অবৈধ (revoke) করা কঠিন।
  2. Token Size:
    • JWT সেশন কুকির চেয়ে বড় হতে পারে, যা কিছু ক্ষেত্রে বেশি ব্যান্ডউইথ ব্যবহার করতে পারে।
  3. Expiration Management:
    • টোকেন মেয়াদ শেষ হওয়ার পরে নতুন টোকেন জেনারেট করতে ক্লায়েন্টের অতিরিক্ত কাজ করতে হয়।

Spring Security-তে JWT এর ব্যবহার:

Step 1: ডিপেনডেন্সি যোগ করুন

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

Step 2: JWT Token Generator তৈরি করুন

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;

public class JwtUtil {

    private static final String SECRET_KEY = "secretKey";

    public static String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60)) // 1 hour validity
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }

    public static boolean validateToken(String token, String username) {
        String extractedUsername = Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
        return extractedUsername.equals(username) && !isTokenExpired(token);
    }

    private static boolean isTokenExpired(String token) {
        Date expiration = Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody()
                .getExpiration();
        return expiration.before(new Date());
    }
}

Step 3: JWT Filter তৈরি করুন

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class JwtFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String authorizationHeader = request.getHeader("Authorization");

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            String token = authorizationHeader.substring(7);
            String username = JwtUtil.extractUsername(token);

            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                if (JwtUtil.validateToken(token, username)) {
                    Authentication auth = JwtUtil.getAuthentication(token);
                    SecurityContextHolder.getContext().setAuthentication(auth);
                }
            }
        }

        filterChain.doFilter(request, response);
    }
}

Step 4: Spring Security Config এ JWT যুক্ত করুন

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/auth/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .addFilter(new JwtFilter());
    }
}

JWT এর জন্য সেরা চর্চা (Best Practices):

  1. Short Token Expiry:
    • টোকেনের মেয়াদ সংক্ষিপ্ত রাখুন এবং রিফ্রেশ টোকেন ব্যবহার করুন।
  2. Secure Secret Key:
    • একটি শক্তিশালী এবং নিরাপদ সিক্রেট কী ব্যবহার করুন।
  3. HTTPS ব্যবহার:
    • সমস্ত টোকেন এনক্রিপ্টেড ট্র্যাফিকে (HTTPS) পাঠান।
  4. Revocation Mechanism:
    • ব্ল্যাকলিস্ট বা কনকারেন্ট সেশন কন্ট্রোল ইমপ্লিমেন্ট করুন।

উপসংহার:

JWT একটি লাইটওয়েট এবং কার্যকর সল্যুশন যা অ্যাপ্লিকেশনগুলিতে Stateless Authentication নিশ্চিত করে। এটি বিশেষত মাইক্রোসার্ভিস এবং মোবাইল অ্যাপ্লিকেশনের মতো ডিস্ট্রিবিউটেড সিস্টেমের জন্য উপযুক্ত। Spring Security-তে JWT সহজেই ইমপ্লিমেন্ট করা যায়, যা Authentication এবং Authorization এর জন্য একটি আধুনিক এবং নিরাপদ সমাধান প্রদান করে।

Content added By

Spring Security তে JWT Authentication কনফিগার করা

121
121

JWT (JSON Web Token) একটি স্ট্যান্ডার্ড টোকেন-ভিত্তিক অথেনটিকেশন মেকানিজম। এটি সাধারণত Stateless Authentication এর জন্য ব্যবহৃত হয়। Spring Security-তে JWT Authentication কনফিগার করলে, সেশন ব্যবস্থাপনা ছাড়াই ব্যবহারকারীর তথ্য যাচাই করা যায়।


JWT Authentication-এর কাজের ধাপ:

  1. User Login:
    • ব্যবহারকারী লগইন করে এবং তার credentials পাঠায়।
    • সার্ভার credentials যাচাই করে একটি JWT Token জেনারেট করে।
  2. JWT Token Validation:
    • প্রতিটি রিকোয়েস্টের সাথে টোকেন পাঠানো হয় (সাধারণত Authorization Header এর মাধ্যমে)।
    • Spring Security টোকেন যাচাই করে ব্যবহারকারীর পরিচয় নিশ্চিত করে।
  3. Access to Resources:
    • টোকেন যাচাই সফল হলে ব্যবহারকারী নির্দিষ্ট রিসোর্সে অ্যাক্সেস পায়।

Spring Security-তে JWT Authentication কনফিগারেশন

Step 1: প্রয়োজনীয় ডিপেন্ডেন্সি যোগ করা

Maven Dependency:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>

Step 2: JWT Utility Class তৈরি করা

JWT Token জেনারেট এবং যাচাই করার জন্য একটি Utility Class:

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;

import java.security.Key;
import java.util.Date;

import org.springframework.stereotype.Component;

@Component
public class JwtUtil {

    private final String SECRET_KEY = "your-256-bit-secret-key"; // 256-bit Key
    private final int EXPIRATION_TIME = 1000 * 60 * 60 * 10; // 10 Hours

    private Key getSigningKey() {
        return Keys.hmacShaKeyFor(SECRET_KEY.getBytes());
    }

    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(getSigningKey(), SignatureAlgorithm.HS256)
                .compact();
    }

    public String extractUsername(String token) {
        return Jwts.parserBuilder()
                .setSigningKey(getSigningKey())
                .build()
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
    }

    public boolean isTokenValid(String token, String username) {
        return username.equals(extractUsername(token)) && !isTokenExpired(token);
    }

    private boolean isTokenExpired(String token) {
        return Jwts.parserBuilder()
                .setSigningKey(getSigningKey())
                .build()
                .parseClaimsJws(token)
                .getBody()
                .getExpiration()
                .before(new Date());
    }
}

Step 3: Custom Authentication Filter তৈরি করা

JWT Token যাচাই করার জন্য একটি Filter তৈরি:

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.authentication.UsernamePasswordAuthenticationToken;
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.isTokenValid(token, userDetails.getUsername())) {
                UsernamePasswordAuthenticationToken authToken =
                        new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authToken);
            }
        }
        filterChain.doFilter(request, response);
    }
}

Step 4: Spring Security Configuration

JWT Authentication যুক্ত করার জন্য Spring Security কনফিগার করা:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception {
        http
            .csrf().disable()
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/login", "/register").permitAll()
                .anyRequest().authenticated()
            )
            .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
        return configuration.getAuthenticationManager();
    }
}

Step 5: Login Controller

লগইন রিকোয়েস্ট থেকে JWT Token জেনারেট করা:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.*;

@RestController
public class AuthController {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtUtil jwtUtil;

    @PostMapping("/login")
    public String login(@RequestBody AuthRequest authRequest) {
        try {
            Authentication auth = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword()));
            return jwtUtil.generateToken(authRequest.getUsername());
        } catch (AuthenticationException e) {
            throw new RuntimeException("Invalid username or password");
        }
    }
}

class AuthRequest {
    private String username;
    private String password;
    // Getters and Setters
}

উপসংহার:

Spring Security-তে JWT Authentication কনফিগার করা একটি শক্তিশালী অথেনটিকেশন প্রক্রিয়া যা Stateless Application-এ ব্যবহৃত হয়।

  1. Token Generation: লগইনের পর JWT টোকেন তৈরি হয়।
  2. Token Validation: প্রতিটি রিকোয়েস্টে টোকেন যাচাই করে।
  3. Security Filter Chain: কাস্টম ফিল্টার ব্যবহার করে টোকেন যাচাই সহজ হয়।

এই কনফিগারেশন অ্যাপ্লিকেশনের নিরাপত্তা নিশ্চিত করে এবং Traditional Session-Based Authentication এর চেয়ে আরও স্কেলেবল।

Content added By

Token Generation, Validation এবং Expiration Management

115
115

Token ভিত্তিক Authentication, বিশেষত JWT (JSON Web Token) ব্যবহার, Spring Security এর মাধ্যমে নিরাপদ এবং কার্যকর অ্যাপ্লিকেশন তৈরি করতে সহায়ক।

Token ব্যবস্থাপনার মূল তিনটি ধাপ হলো:

  1. Token Generation (টোকেন তৈরি)
  2. Token Validation (টোকেন যাচাই)
  3. Token Expiration Management (টোকেনের মেয়াদ পরিচালনা)

১. Token Generation (টোকেন তৈরি)

Token তৈরি করার জন্য সাধারণত JWT ব্যবহার করা হয়। এটি একটি কমপ্যাক্ট এবং স্বাক্ষরযুক্ত স্ট্রিং যা ব্যবহারকারীর তথ্য বহন করে।

Token Structure:

JWT তিনটি অংশ নিয়ে গঠিত:

  1. Header:
    • এলগরিদম এবং টোকেনের ধরন।
  2. Payload:
    • ব্যবহারকারীর তথ্য যেমন username, roles, এবং অন্যান্য ক্লেইম।
  3. Signature:
    • টোকেনের অখণ্ডতা নিশ্চিত করার জন্য সিক্রেট কী দিয়ে সাইন করা হয়।

Spring Boot ব্যবহার করে JWT টোকেন তৈরি করা:

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;

public class JwtUtil {

    private static final String SECRET_KEY = "mySecretKey";
    private static final long EXPIRATION_TIME = 86400000; // 1 দিন

    public static 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();
    }
}

২. Token Validation (টোকেন যাচাই)

Token যাচাই করার সময় নিশ্চিত করতে হবে:

  • টোকেনটি বৈধ।
  • টোকেনের স্বাক্ষর সঠিক।
  • টোকেন মেয়াদোত্তীর্ণ নয়।

Spring Boot ব্যবহার করে JWT যাচাই:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;

public class JwtUtil {

    private static final String SECRET_KEY = "mySecretKey";

    public static Claims validateToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(SECRET_KEY)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (SignatureException e) {
            throw new RuntimeException("Invalid JWT Signature");
        } catch (Exception e) {
            throw new RuntimeException("Invalid Token");
        }
    }
}

Token Validation ব্যবহার:

String token = "Bearer eyJhbGciOiJIUzI1NiIsInR...";
Claims claims = JwtUtil.validateToken(token.substring(7));
String username = claims.getSubject();

৩. Token Expiration Management (টোকেনের মেয়াদ পরিচালনা)

Token এর মেয়াদ নির্ধারণ করা নিরাপত্তার একটি গুরুত্বপূর্ণ অংশ। মেয়াদোত্তীর্ণ টোকেন গ্রহণ করা হলে এটি নিরাপত্তা ঝুঁকি তৈরি করতে পারে।

Expiration Time সেটআপ:

Token তৈরি করার সময় setExpiration ফিল্ড ব্যবহার করে মেয়াদ নির্ধারণ করা হয়।

private static final long EXPIRATION_TIME = 3600000; // 1 ঘন্টা

Expiration যাচাই:

Spring Security স্বয়ংক্রিয়ভাবে মেয়াদোত্তীর্ণ টোকেনের জন্য একটি ExpiredJwtException ছুঁড়ে দেয়। এটি যাচাই করার জন্য:

public static boolean isTokenExpired(String token) {
    Claims claims = validateToken(token);
    return claims.getExpiration().before(new Date());
}

Spring Security এর সাথে JWT Token Integration:

Authentication Filter:

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String token = request.getHeader("Authorization");

        if (token != null && token.startsWith("Bearer ")) {
            token = token.substring(7);

            if (!JwtUtil.isTokenExpired(token)) {
                Claims claims = JwtUtil.validateToken(token);
                String username = claims.getSubject();

                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                        username, null, new ArrayList<>());
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }

        filterChain.doFilter(request, response);
    }
}

Spring Security Configuration:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
                .antMatchers("/login", "/register").permitAll()
                .anyRequest().authenticated()
            .and()
            .addFilter(new JwtAuthenticationFilter());
    }
}

Token Refresh এবং Revocation Management:

  1. Token Refresh:
    • মেয়াদ শেষ হওয়ার আগে নতুন টোকেন ইস্যু করার একটি পদ্ধতি।
    • Spring Security এ একটি /refresh এন্ডপয়েন্ট ব্যবহার করা যায়।
  2. Token Revocation:
    • ব্যবহারকারীর লগআউট বা টোকেন অবৈধ করার জন্য টোকেন রোকেশন প্রয়োজন।
    • টোকেনগুলো ব্ল্যাকলিস্টে সংরক্ষণ করা যেতে পারে।

সারাংশ:

Token Generation:

  • টোকেন তৈরি করার সময় Header, Payload, এবং Signature সঠিকভাবে সংজ্ঞায়িত করুন।

Token Validation:

  • টোকেনের বৈধতা এবং মেয়াদ যাচাই করুন।

Token Expiration Management:

  • স্বল্পমেয়াদী টোকেন ব্যবহার করুন এবং প্রয়োজন হলে রিফ্রেশ টোকেন ইস্যু করুন।

Spring Security এর মাধ্যমে Token Based Authentication সহজেই সুরক্ষিত এবং স্কেলেবল অ্যাপ্লিকেশন তৈরি করা যায়।

Content added By

উদাহরণ সহ JWT Authentication এবং Authorization

101
101

JWT (JSON Web Token) হল একটি স্ট্যান্ডার্ড টোকেন-ভিত্তিক প্রমাণীকরণ এবং অনুমোদনের পদ্ধতি, যেখানে একটি কমপ্যাক্ট এবং সেলফ-কনটেইনড টোকেন ব্যবহার করা হয়। Spring Security-তে JWT Authentication এবং Authorization কনফিগার করার জন্য নিচে একটি স্টেপ-বাই-স্টেপ উদাহরণ দেওয়া হলো।


1. JWT-এর মূল বৈশিষ্ট্য

  • Stateless Authentication: সার্ভার সেশন মেইনটেইন করে না, বরং ক্লায়েন্ট-সাইডে টোকেন সংরক্ষণ করে।
  • Self-contained Token: টোকেনে সমস্ত তথ্য এনকোড করা থাকে (যেমন ইউজার আইডি, রোল)।
  • Security: টোকেনটি সাইন করা থাকে যাতে এটি পরিবর্তন করা না যায়।

2. প্রজেক্ট ডিপেন্ডেন্সি

Spring Boot এবং JWT লাইব্রেরি যুক্ত করতে pom.xml বা build.gradle-এ ডিপেন্ডেন্সি যোগ করুন।

Maven Dependency

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

Gradle Dependency

implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'io.jsonwebtoken:jjwt:0.9.1'

3. JWT Token তৈরি এবং যাচাই করার ক্লাস

i. JWT 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 JwtUtil {

    private String secretKey = "mySecretKey";
    private long expirationTime = 1000 * 60 * 60; // 1 hour

    // টোকেন তৈরি
    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expirationTime))
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
    }

    // টোকেন থেকে ইউজারনেম বের করা
    public String extractUsername(String token) {
        return getClaims(token).getSubject();
    }

    // টোকেন এক্সপায়ার্ড কিনা যাচাই
    public boolean isTokenExpired(String token) {
        return getClaims(token).getExpiration().before(new Date());
    }

    // টোকেন যাচাই
    public boolean validateToken(String token, String username) {
        return (username.equals(extractUsername(token)) && !isTokenExpired(token));
    }

    // প্রাইভেট মেথড: Claims বের করা
    private Claims getClaims(String token) {
        return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
    }
}

4. Spring Security Configuration

i. Security Config ক্লাস

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
                .antMatchers("/authenticate", "/register").permitAll() // Public endpoints
                .anyRequest().authenticated(); // Others require authentication
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("user").password(passwordEncoder().encode("password")).roles("USER")
            .and()
            .withUser("admin").password(passwordEncoder().encode("admin")).roles("ADMIN");
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

5. Authentication Controller

i. Token প্রদান করার জন্য Controller

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;

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

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtUtil jwtUtil;

    @PostMapping
    public String generateToken(@RequestBody AuthRequest authRequest) {
        try {
            Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
            );
            return jwtUtil.generateToken(authRequest.getUsername());
        } catch (Exception e) {
            throw new RuntimeException("Invalid username/password");
        }
    }
}

// DTO for authentication request
class AuthRequest {
    private String username;
    private String password;

    // Getters and Setters
}

6. JWT Filter

i. JWT Token যাচাই করার জন্য Filter

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class JwtFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
        String authorizationHeader = request.getHeader("Authorization");

        String token = null;
        String username = null;

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            token = authorizationHeader.substring(7);
            username = jwtUtil.extractUsername(token);
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            if (jwtUtil.validateToken(token, username)) {
                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                        username, null, new ArrayList<>());
                authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authToken);
            }
        }
        chain.doFilter(request, response);
    }
}

7. Filter Configuration

i. Filter Spring Security Config-এ যুক্ত করা

import org.springframework.beans.factory.annotation.Autowired;
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.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtFilter jwtFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
                .antMatchers("/authenticate").permitAll()
                .anyRequest().authenticated()
                .and()
            .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

8. Test Endpoints

Public Endpoint:

@GetMapping("/public")
public String publicEndpoint() {
    return "This is a public endpoint";
}

Secured Endpoint:

@GetMapping("/secured")
public String securedEndpoint() {
    return "This is a secured endpoint";
}

JWT ব্যবহার করার প্রক্রিয়া

  1. /authenticatePOST রিকোয়েস্ট পাঠিয়ে টোকেন জেনারেট করুন।
  2. টোকেনটি প্রতিটি সিকিউরড রিকোয়েস্টে Authorization HeaderBearer <token> আকারে পাঠান।
  3. সিকিউরড এন্ডপয়েন্টে এক্সেস পান।

উপসংহার

Spring Security-তে JWT Authentication এবং Authorization ইমপ্লিমেন্ট করলে আপনার অ্যাপ্লিকেশন stateless এবং নিরাপদ হয়। উপরোক্ত উদাহরণটি ব্যবহার করে আপনি সহজেই একটি JWT-ভিত্তিক Authentication সিস্টেম তৈরি করতে পারবেন।

Content added By
Promotion