Spring Security + JWT: अपनी Application को एक किले की तरह सुरक्षित रखना
Spring Security और JWT authentication के लिए एक पूर्ण guide — filter chain, JWT token flow, BCrypt password hashing, SecurityFilterChain configuration, @PreAuthorize के साथ method-level security, OAuth2 मूल बातें, CORS, CSRF protection, और RBAC को कवर करता है।
Security क्यों मायने रखती है: आपके घर पर ताले
कल्पना कीजिए आपकी application एक घर है। Security के बिना, कोई भी अंदर आ सकता है, आपका fridge खोल सकता है, आपकी diary पढ़ सकता है, और दरवाजा खुला छोड़ सकता है। Spring Security पूर्ण home security system है — हर दरवाजे पर ताले, हर कमरे पर नज़र रखने वाले cameras, और gate पर एक guard जो किसी को भी अंदर आने देने से पहले IDs check करता है।
हर web application तीन core security सवालों का सामना करती है:
- Authentication: आप कौन हैं? (दरवाजे पर अपना ID दिखाएँ)
- Authorization: आपको क्या करने की अनुमति है? (Guests living room में प्रवेश कर सकते हैं, लेकिन केवल परिवार ऊपर जाता है)
- Protection: क्या दीवारें काफी मजबूत हैं? (CSRF, XSS, और session hijacking जैसे break-ins को रोकना)
Spring Security तीनों के जवाब देता है — और यह एक शक्तिशाली design के साथ करता है जिसे Filter Chain कहा जाता है।
Security Filter Chain: आपकी Application की Assembly Line
एक factory assembly line के बारे में सोचें। प्रत्येक item (HTTP request) stations (filters) से क्रम में passes करता है। प्रत्येक station एक चीज़ check करता है। यदि कोई check विफल होता है, तो item reject हो जाता है और कभी end तक नहीं पहुँचता। यदि यह सभी stations पास करता है, तो यह आपके controller पर पहुँचता है।
यहाँ है वह क्रम जिसमें Spring Security हर request को process करता है:
HTTP Request
|
v
[CorsFilter] -- Is this request from an allowed website?
|
v
[CsrfFilter] -- Does this request have a valid CSRF token?
|
v
[JwtAuthFilter] -- Does this request carry a valid JWT token?
|
v
[AuthorizationFilter] -- Does this user have permission for this endpoint?
|
v
[Your Controller] -- Finally! Process the business logic.
यदि कोई filter "नहीं" कहता है, तो request वहीं रुक जाती है। आपका controller इसे कभी देखता भी नहीं है। यह defense in depth है — protection की कई layers, जैसे एक castle जिसमें दीवारें, एक खाई, और towers पर archers हैं।
JWT: आपका Digital Movie Ticket
एक JSON Web Token (JWT) एक movie ticket की तरह है। जब आप एक movie ticket खरीदते हैं, तो उस पर आपका seat number, show time, और theater number printed होता है। दरवाजे पर usher box office को verify करने के लिए call नहीं करता कि आपने भुगतान किया था — वे बस आपके ticket को देखते हैं, check करते हैं कि यह valid है, और आपको अंदर आने देते हैं।
JWT उसी तरह काम करता है:
- आप अपने username और password के साथ log in करते हैं (ticket खरीदें)
- Server आपको एक JWT token देता है (ticket स्वयं)
- हर भविष्य की request में, आप token दिखाते हैं (usher पर ticket flash करें)
- Server database check किए बिना token को validate करता है (usher ticket पढ़ता है)
एक JWT के तीन भाग dots से अलग होते हैं: header.payload.signature
// A JWT looks like this (three Base64 chunks separated by dots):
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJqb2huIiwicm9sZXMiOlsiVVNFUiJdLCJpYXQiOjE3MDAwMDAwMDAsImV4cCI6MTcwMDAzNjAwMH0.abc123signature
// Decoded payload:
{
"sub": "john", // subject (username)
"roles": ["USER"], // what this person can do
"iat": 1700000000, // issued at (when the ticket was printed)
"exp": 1700036000 // expires at (when the ticket stops working)
}
Signature security seal है। Server token को एक secret key के साथ sign करता है। यदि कोई payload के साथ छेड़छाड़ करता है ("USER" को "ADMIN" में बदलने की कोशिश करता है), तो signature match नहीं होगा, और server इसे reject कर देता है। यह आपके ticket पर एक hologram की तरह है — इसे forge करें, और आप पकड़े जाते हैं।
पूर्ण JWT Authentication Flow
आइए पूरे login-to-access flow को step by step walk through करें।
Step 1: User Entity
पहले, हमें एक User class की आवश्यकता है जो credentials को database में store करे।
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String username;
@Column(nullable = false)
private String password; // stored as BCrypt hash, NEVER plain text
@Column(nullable = false)
private String role; // "USER", "ADMIN", "MODERATOR"
@Column(nullable = false)
private String email;
// getters, setters, constructors
}
Step 2: BCrypt Password Hashing
Passwords को plain text में store करना आपके debit card पर अपना PIN लिखने की तरह है। यदि कोई database चुराता है, तो उन्हें हर password तुरंत मिल जाता है। BCrypt एक one-way scrambler है — यह "mypassword123" को एक लंबे, random दिखने वाले string में बदल देता है। भले ही दो users के पास समान password हो, BCrypt अलग hashes उत्पन्न करता है क्योंकि यह हर बार random "salt" जोड़ता है।
@Configuration
public class PasswordConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12); // 12 rounds of hashing
}
}
// What BCrypt does:
// "mypassword123" -> "$2a$12$LJ3m4ys8Hp5Rq.Xz9vKnet..."
// Same password again -> "$2a$12$R8pQw7Yt.N2mFkA5dB3kZu..." (different hash!)
जब एक user log in करता है, तो BCrypt store किए गए hash को "decode" नहीं करता है। इसके बजाय, यह submitted password को उसी तरह hash करता है और results की तुलना करता है। यदि वे match होते हैं, तो password सही है।
Step 3: UserDetailsService — Spring को सिखाना कि आपके Users कौन हैं
Spring Security को नहीं पता कि आपके users कहाँ रहते हैं — एक database में? एक file में? एक API में? आप UserDetailsService को implement करके इसे बताते हैं।
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(
"User not found: " + username));
return org.springframework.security.core.userdetails.User.builder()
.username(user.getUsername())
.password(user.getPassword()) // BCrypt hash from DB
.roles(user.getRole()) // "USER" becomes ROLE_USER
.build();
}
}
Step 4: JWT Utility — Tokens Create और Validate करना
यह class ticket printer और ticket checker दोनों एक साथ है।
@Component
public class JwtUtil {
@Value("${jwt.secret}")
private String secretKey;
@Value("${jwt.expiration:3600000}") // default: 1 hour in milliseconds
private long expiration;
// Print a new ticket
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put("roles", userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList()));
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
// Generate a refresh token (longer-lived)
public String generateRefreshToken(UserDetails userDetails) {
return Jwts.builder()
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration * 24)) // 24 hours
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
// Read the username from the ticket
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
// Check if the ticket is still valid
public boolean isTokenValid(String token, UserDetails userDetails) {
String username = extractUsername(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
private boolean isTokenExpired(String token) {
return extractClaim(token, Claims::getExpiration).before(new Date());
}
private <T> T extractClaim(String token, Function<Claims, T> resolver) {
Claims claims = Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
return resolver.apply(claims);
}
private Key getSigningKey() {
byte[] keyBytes = Decoders.BASE64.decode(secretKey);
return Keys.hmacShaKeyFor(keyBytes);
}
}
Step 5: JWT Authentication Filter — Usher
यह filter हर request पर चलता है। यह check करता है: क्या इस request में एक valid JWT है? यदि हाँ, तो यह Spring Security को बताता है "यह व्यक्ति authenticated है।" यदि नहीं, तो यह कुछ नहीं करता और अगले filter को इसे संभालने देता है।
@Component
public class JwtAuthFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
private final UserDetailsService userDetailsService;
public JwtAuthFilter(JwtUtil jwtUtil, UserDetailsService userDetailsService) {
this.jwtUtil = jwtUtil;
this.userDetailsService = userDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
// 1. Get the Authorization header
String authHeader = request.getHeader("Authorization");
// 2. No token? Let the request continue (might be a public endpoint)
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
// 3. Extract the token (remove "Bearer " prefix)
String token = authHeader.substring(7);
String username = jwtUtil.extractUsername(token);
// 4. If we got a username and no one is authenticated yet...
if (username != null &&
SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
// 5. Validate the token
if (jwtUtil.isTokenValid(token, userDetails)) {
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authToken.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request));
// 6. Tell Spring Security: this user is authenticated
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
filterChain.doFilter(request, response);
}
}
Step 6: Authentication Controller — Login और Refresh
@RestController
@RequestMapping("/api/auth")
public class AuthController {
private final AuthenticationManager authManager;
private final JwtUtil jwtUtil;
private final UserDetailsService userDetailsService;
private final PasswordEncoder passwordEncoder;
private final UserRepository userRepository;
// constructor injection for all dependencies
@PostMapping("/register")
public ResponseEntity<String> register(@RequestBody RegisterRequest request) {
User user = new User();
user.setUsername(request.getUsername());
user.setPassword(passwordEncoder.encode(request.getPassword())); // BCrypt hash
user.setEmail(request.getEmail());
user.setRole("USER"); // default role
userRepository.save(user);
return ResponseEntity.ok("User registered successfully");
}
@PostMapping("/login")
public ResponseEntity<AuthResponse> login(@RequestBody LoginRequest request) {
// Spring Security verifies username + password
authManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.getUsername(), request.getPassword()));
UserDetails userDetails = userDetailsService
.loadUserByUsername(request.getUsername());
String accessToken = jwtUtil.generateToken(userDetails);
String refreshToken = jwtUtil.generateRefreshToken(userDetails);
return ResponseEntity.ok(new AuthResponse(accessToken, refreshToken));
}
@PostMapping("/refresh")
public ResponseEntity<AuthResponse> refresh(@RequestBody RefreshRequest request) {
String username = jwtUtil.extractUsername(request.getRefreshToken());
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtUtil.isTokenValid(request.getRefreshToken(), userDetails)) {
String newAccessToken = jwtUtil.generateToken(userDetails);
return ResponseEntity.ok(
new AuthResponse(newAccessToken, request.getRefreshToken()));
}
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
}
SecurityFilterChain Configuration: सब कुछ एक साथ Wire करना
यह master control panel है। यह Spring Security को बताता है कि कौन से endpoints public हैं, कौन से authentication की आवश्यकता है, और chain में JWT filter कहाँ रखना है।
@Configuration
@EnableWebSecurity
@EnableMethodSecurity // enables @PreAuthorize and @Secured
public class SecurityConfig {
private final JwtAuthFilter jwtAuthFilter;
private final UserDetailsService userDetailsService;
private final PasswordEncoder passwordEncoder;
public SecurityConfig(JwtAuthFilter jwtAuthFilter,
UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder) {
this.jwtAuthFilter = jwtAuthFilter;
this.userDetailsService = userDetailsService;
this.passwordEncoder = passwordEncoder;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
// Disable CSRF for stateless JWT APIs (no session = no CSRF risk)
.csrf(csrf -> csrf.disable())
// Configure CORS
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
// Set session management to stateless (no server-side sessions)
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// Define which endpoints are public vs protected
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll() // login, register
.requestMatchers("/api/public/**").permitAll() // public content
.requestMatchers("/api/admin/**").hasRole("ADMIN") // admin only
.anyRequest().authenticated() // everything else needs auth
)
// Set the authentication provider
.authenticationProvider(authenticationProvider())
// Add JWT filter BEFORE Spring's username/password filter
.addFilterBefore(jwtAuthFilter,
UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder);
return provider;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config)
throws Exception {
return config.getAuthenticationManager();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("http://localhost:3000"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
config.setAllowedHeaders(List.of("Authorization", "Content-Type"));
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
}
Method-Level Security: Fine-Grained Access Control
SecurityFilterChain URL स्तर पर access नियंत्रित करता है। लेकिन कभी-कभी आपको और बारीक नियंत्रण की आवश्यकता होती है — जैसे कहना "केवल admins delete कर सकते हैं, लेकिन कोई भी authenticated user पढ़ सकता है।" तभी @PreAuthorize और @Secured काम आते हैं।
@RestController
@RequestMapping("/api/posts")
public class BlogPostController {
private final BlogPostService postService;
// Anyone authenticated can read
@GetMapping
public List<BlogPost> getAllPosts() {
return postService.findAll();
}
// Only the post author or admins can update
@PreAuthorize("hasRole('ADMIN') or #postId == authentication.principal.id")
@PutMapping("/{postId}")
public BlogPost updatePost(@PathVariable Long postId,
@RequestBody BlogPost post) {
return postService.update(postId, post);
}
// Only admins can delete
@PreAuthorize("hasRole('ADMIN')")
@DeleteMapping("/{postId}")
public void deletePost(@PathVariable Long postId) {
postService.delete(postId);
}
// Only moderators and admins
@Secured({"ROLE_MODERATOR", "ROLE_ADMIN"})
@PutMapping("/{postId}/approve")
public BlogPost approvePost(@PathVariable Long postId) {
return postService.approve(postId);
}
// SpEL expression: user can only see their own comments
@PreAuthorize("#username == authentication.name")
@GetMapping("/user/{username}/comments")
public List<Comment> getUserComments(@PathVariable String username) {
return postService.getCommentsByUser(username);
}
}
मुख्य अंतर: @PreAuthorize SpEL (Spring Expression Language) का उपयोग करता है और method parameters तक पहुँच सकता है। @Secured सरल है — यह केवल roles check करता है। जब आपको expressions की आवश्यकता हो तो @PreAuthorize का उपयोग करें, जब आपको केवल role checks की आवश्यकता हो तो @Secured का उपयोग करें।
Role-Based Access Control (RBAC): Permissions का पदानुक्रम
RBAC एक office building में key card system की तरह है। एक intern का card front door खोलता है। एक manager का card front door plus meeting rooms खोलता है। CEO का card सब कुछ खोलता है। प्रत्येक role permissions का एक set grant करता है, और उच्च roles में सभी निम्न roles की permissions शामिल होती हैं।
// Define roles as an enum
public enum AppRole {
USER, // can read posts, write comments
MODERATOR, // USER + approve/reject comments
ADMIN // MODERATOR + delete posts, manage users
}
// Role hierarchy configuration
@Bean
public RoleHierarchy roleHierarchy() {
return RoleHierarchyImpl.withDefaultRolePrefix()
.role("ADMIN").implies("MODERATOR")
.role("MODERATOR").implies("USER")
.build();
}
// Now @PreAuthorize("hasRole('USER')") allows MODERATOR and ADMIN too
CORS: आपके API से कौन बात कर सकता है?
कल्पना कीजिए आपका API एक private phone line है। CORS (Cross-Origin Resource Sharing) caller ID system है — यह check करता है कि call कहाँ से आ रही है और तय करता है कि उठाना है या नहीं।
CORS configuration के बिना, browsers विभिन्न domains से requests को block करते हैं। localhost:3000 पर आपका React app localhost:8080 पर आपके Spring API से बात नहीं कर सकता जब तक कि आप स्पष्ट रूप से इसे allow नहीं करते।
// Option 1: Global CORS in SecurityConfig (shown above)
// Option 2: Per-controller CORS
@CrossOrigin(origins = "http://localhost:3000", maxAge = 3600)
@RestController
@RequestMapping("/api/posts")
public class BlogPostController {
// all endpoints in this controller allow localhost:3000
}
// Option 3: application.yml configuration
// spring:
// web:
// cors:
// allowed-origins: http://localhost:3000
// allowed-methods: GET, POST, PUT, DELETE
// allowed-headers: Authorization, Content-Type
CSRF Protection: अदृश्य Attacks को रोकना
CSRF ऐसा है जैसे कोई आपकी जेब में एक signed check डाल दे। आप एक malicious website visit करते हैं, और यह आपके मौजूदा session cookies का उपयोग करके गुप्त रूप से आपके bank को एक request भेजता है। Bank सोचता है कि यह आप हैं क्योंकि cookies valid हैं।
JWT-आधारित APIs के लिए, CSRF एक चिंता नहीं है क्योंकि हम authentication के लिए cookies का उपयोग नहीं करते। JWT Authorization header में भेजा जाता है, और browsers स्वचालित रूप से headers attach नहीं करते जैसे वे cookies के साथ करते हैं। इसीलिए हम अपने config में CSRF को disable करते हैं: .csrf(csrf -> csrf.disable())।
Session-आधारित apps के लिए (Thymeleaf के साथ server-rendered pages), CSRF protection महत्वपूर्ण है:
// Keep CSRF enabled for session-based apps
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler()))
// In your Thymeleaf form, Spring adds the token automatically:
// <form th:action="@{/api/posts}" method="post">
// <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
// ...
// </form>
OAuth2 मूल बातें: Google और GitHub को Login संभालने दें
OAuth2 एक hotel में check in करने के लिए अपने driver's license का उपयोग करने की तरह है। Hotel आपको एक ID issue नहीं करता — वे सरकार (Google, GitHub) पर भरोसा करते हैं जिसने आपका license issue किया था। आप एक trusted third party के माध्यम से साबित करते हैं कि आप कौन हैं।
Spring Security OAuth2 login को लगभग trivial बनाता है:
// 1. Add the dependency
// <dependency>
// <groupId>org.springframework.boot</groupId>
// <artifactId>spring-boot-starter-oauth2-client</artifactId>
// </dependency>
// 2. Configure in application.yml
// spring:
// security:
// oauth2:
// client:
// registration:
// google:
// client-id: ${GOOGLE_CLIENT_ID}
// client-secret: ${GOOGLE_CLIENT_SECRET}
// scope: openid, profile, email
// github:
// client-id: ${GITHUB_CLIENT_ID}
// client-secret: ${GITHUB_CLIENT_SECRET}
// scope: user:email
// 3. Update SecurityFilterChain
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.oauth2Login(oauth -> oauth
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.userInfoEndpoint(userInfo -> userInfo
.userService(customOAuth2UserService)))
.build();
}
OAuth2 flow चार steps में काम करता है: (1) User "Sign in with Google" पर click करता है, (2) Browser Google पर redirect करता है, (3) User approve करता है, Google एक authorization code वापस भेजता है, (4) आपका server user info के लिए code exchange करता है। Spring steps 2-4 को स्वचालित रूप से संभालता है।
पूर्ण application.yml Security Configuration
# application.yml
jwt:
secret: ${JWT_SECRET:myBase64EncodedSecretKeyThatIsAtLeast256BitsLong==}
expiration: 3600000 # 1 hour in milliseconds
refresh-expiration: 86400000 # 24 hours
spring:
datasource:
url: jdbc:postgresql://localhost:5432/myapp
username: ${DB_USERNAME:postgres}
password: ${DB_PASSWORD:postgres}
jpa:
hibernate:
ddl-auto: validate # never use create/update in production
show-sql: false
# CORS settings
app:
cors:
allowed-origins: http://localhost:3000,http://localhost:3001
allowed-methods: GET,POST,PUT,DELETE,OPTIONS
max-age: 3600
# Logging security events
logging:
level:
org.springframework.security: DEBUG # turn this off in production
com.example.security: INFO
Production Security Checklist
- हर जगह HTTPS का उपयोग करें (JWTs को कभी plain HTTP पर न भेजें)
- Short access token expiration set करें (15-60 मिनट)
- Refresh tokens को सुरक्षित रूप से store करें (httpOnly cookies या encrypted database)
- एक मजबूत, random JWT secret का उपयोग करें (कम से कम 256 bits)
- Password hashing के लिए 10-12 rounds के साथ BCrypt
- Brute force attacks को रोकने के लिए login endpoints को rate limit करें
- Monitoring के लिए authentication failures को log करें
- कभी भी error responses में stack traces expose न करें
- सभी input को validate करें — security request body पर शुरू होती है
- Dependencies को updated रखें — security patches मायने रखते हैं
अक्सर पूछे जाने वाले प्रश्न
1. Authentication और authorization के बीच क्या अंतर है?
Authentication यह साबित करना है कि आप कौन हैं — हवाई अड्डे पर अपना ID दिखाने की तरह। Authorization यह साबित करना है कि आपको क्या करने की अनुमति है — आपका boarding pass जो कहता है कि आप flight 42 पर board कर सकते हैं लेकिन flight 99 पर नहीं। Spring Security पहले authentication संभालता है (UserDetailsService और AuthenticationManager के माध्यम से), फिर authorization (SecurityFilterChain rules और @PreAuthorize के माध्यम से)। आपको authorized होने से पहले authenticated होना चाहिए।
2. हम JWT-आधारित APIs के लिए CSRF को क्यों disable करते हैं?
CSRF attacks आपके browser को requests के साथ cookies को स्वचालित रूप से भेजने के लिए trick करके काम करते हैं। चूँकि JWT tokens memory में store होते हैं (cookies में नहीं) और manually Authorization header में भेजे जाते हैं, browser को उन्हें भेजने के लिए trick नहीं किया जा सकता। कोई automatic cookies नहीं का मतलब है कोई CSRF risk नहीं। हालाँकि, यदि आप JWTs को cookies में store करते हैं (जो कुछ applications करती हैं), तो आपको CSRF protection enabled रखना होगा।
3. जब access token expire हो जाता है तो क्या होता है?
जब access token expire होता है, तो server 401 Unauthorized response returns करता है। Client को फिर refresh token को /api/auth/refresh endpoint पर भेजना चाहिए ताकि एक नया access token मिल सके — user से फिर से log in करने के लिए पूछे बिना। इसे इस तरह सोचें: access token एक theme park में एक दिन का pass है (short-lived), और refresh token आपका season pass है (long-lived)। जब आपका day pass expire होता है, तो आप एक नया day pass पाने के लिए अपना season pass दिखाते हैं।
4. मुझे @PreAuthorize या @Secured का उपयोग करना चाहिए?
अधिकांश मामलों में @PreAuthorize का उपयोग करें क्योंकि यह SpEL expressions का समर्थन करता है, जो आपको "केवल इस post का author इसे edit कर सकता है" (@PreAuthorize("#postId == authentication.principal.id")) जैसी conditions लिखने देता है। @Secured का उपयोग तब करें जब आपको केवल सरल role checks की आवश्यकता हो और चाहते हों कि annotation अधिक readable हो। आप @Secured में Spring Expression Language को mix नहीं कर सकते — यह केवल role names स्वीकार करता है।
5. BCrypt password hashing के लिए SHA-256 से कैसे अलग है?
SHA-256 एक general-purpose hash है — यह design के अनुसार तेज़ है, जिसका अर्थ है कि attackers प्रति सेकंड अरबों passwords try कर सकते हैं। BCrypt जानबूझकर धीमा है। यह विशेष रूप से passwords के लिए designed है और इसमें एक "cost factor" (rounds की संख्या) शामिल है जो प्रत्येक hash को अधिक समय लेने पर मजबूर करता है। 12 rounds पर BCrypt के साथ, एक attacker जो एक अरब passwords try कर रहा है, सेकंड के बजाय सदियाँ लेगा। BCrypt स्वचालित रूप से प्रत्येक hash में एक random "salt" भी जोड़ता है, इसलिए समान password वाले दो users को अलग hashes मिलते हैं।