Spring Security + JWT: మీ అప్లికేషన్ను కోట లాగా రక్షించడం
Spring Security మరియు JWT authentication కోసం సంపూర్ణ గైడ్ — filter chain, JWT token flow, BCrypt password hashing, SecurityFilterChain configuration, @PreAuthorize తో method-level security, OAuth2 basics, CORS, CSRF protection, మరియు RBAC.
Security ఎందుకు ముఖ్యం: మీ ఇంటికి తాళాలు
మీ application ఒక ఇల్లు అని ఊహించుకోండి. Security లేకుండా, ఎవరైనా లోపలికి నడవగలరు, మీ fridge తెరవగలరు, మీ diary చదవగలరు, మరియు తలుపు బార్లా తెరిచి వెళ్లిపోగలరు. Spring Security పూర్తి home security system — ప్రతి తలుపుకు తాళాలు, ప్రతి గదిలో cameras, మరియు gate వద్ద guard ఎవరినైనా లోపలికి పంపే ముందు IDs చెక్ చేస్తాడు.
ప్రతి web application మూడు core security ప్రశ్నలను ఎదుర్కొంటుంది:
- Authentication: మీరు ఎవరు? (తలుపు వద్ద మీ ID చూపించండి)
- Authorization: మీరు ఏమి చేయడానికి అనుమతించబడ్డారు? (Guests living room లోకి ప్రవేశించగలరు, కానీ కుటుంబం మాత్రమే upstairs వెళ్ళగలరు)
- Protection: గోడలు తగినంత బలంగా ఉన్నాయా? (CSRF, XSS, మరియు session hijacking వంటి break-ins ను నిరోధించండి)
Spring Security మూడింటికీ సమాధానం ఇస్తుంది — మరియు ఇది Filter Chain అనే శక్తివంతమైన design తో చేస్తుంది.
Security Filter Chain: మీ Application యొక్క Assembly Line
ఒక factory assembly line గురించి ఆలోచించండి. ప్రతి item (HTTP request) క్రమంలో stations (filters) గుండా వెళ్తుంది. ప్రతి station ఒక విషయాన్ని check చేస్తుంది. ఏదైనా check fail అయితే, item reject చేయబడుతుంది మరియు చివరికి చేరదు. అన్ని stations pass అయితే, అది మీ controller కి చేరుతుంది.
Spring Security ప్రతి request ను process చేసే క్రమం ఇదిగో:
HTTP Request
|
v
[CorsFilter] -- ఈ request అనుమతించబడిన website నుండి వచ్చిందా?
|
v
[CsrfFilter] -- ఈ request కి valid CSRF token ఉందా?
|
v
[JwtAuthFilter] -- ఈ request valid JWT token కలిగి ఉందా?
|
v
[AuthorizationFilter] -- ఈ user కి ఈ endpoint కోసం permission ఉందా?
|
v
[Your Controller] -- చివరగా! Business logic ను process చేయండి.
ఏదైనా filter "no" అంటే, request అక్కడే ఆగిపోతుంది. మీ controller దాన్ని చూడదు కూడా. ఇది defense in depth — అనేక రక్షణ పొరలు, గోడలు, moat, మరియు towers పై archers ఉన్న castle లాగా.
JWT: మీ డిజిటల్ Movie Ticket
JSON Web Token (JWT) ఒక movie ticket లాంటిది. మీరు movie ticket కొన్నప్పుడు, దానిపై మీ seat number, show time, మరియు theater number ముద్రించబడి ఉంటుంది. తలుపు వద్ద usher మీరు pay చేశారో verify చేయడానికి box office కి call చేయడు — అతను మీ ticket చూసి, అది valid అని check చేసి, మిమ్మల్ని లోపలికి పంపుతాడు.
JWT అదే విధంగా పనిచేస్తుంది:
- మీరు username మరియు password తో log in అవుతారు (ticket కొనడం)
- Server మీకు JWT token ఇస్తుంది (ticket itself)
- ప్రతి తదుపరి request లో, మీరు token చూపిస్తారు (usher కి ticket flash చేయడం)
- Server database check చేయకుండా token validate చేస్తుంది (usher ticket చదవడం)
JWT లో dots తో వేరు చేయబడిన మూడు భాగాలు ఉంటాయి: header.payload.signature
// JWT ఇలా కనిపిస్తుంది (dots తో వేరు చేయబడిన మూడు Base64 chunks):
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJqb2huIiwicm9sZXMiOlsiVVNFUiJdLCJpYXQiOjE3MDAwMDAwMDAsImV4cCI6MTcwMDAzNjAwMH0.abc123signature
// Decoded payload:
{
"sub": "john", // subject (username)
"roles": ["USER"], // ఈ వ్యక్తి ఏమి చేయగలరు
"iat": 1700000000, // issued at (ticket ఎప్పుడు print అయింది)
"exp": 1700036000 // expires at (ticket ఎప్పుడు పనిచేయడం ఆగిపోతుంది)
}
Signature అనేది security seal. Server ఒక secret key తో token ను sign చేస్తుంది. ఎవరైనా payload తో tamper చేస్తే ("USER" ను "ADMIN" గా మార్చడానికి ప్రయత్నిస్తే), signature match కాదు, మరియు server దాన్ని reject చేస్తుంది. ఇది మీ ticket పై hologram లాంటిది — దాన్ని forge చేయండి, మరియు మీరు పట్టుబడతారు.
పూర్తి JWT Authentication Flow
మొత్తం login-to-access flow ను step by step చూద్దాం.
Step 1: User Entity
మొదట, database లో credentials store చేసే User class అవసరం.
@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; // BCrypt hash గా store అవుతుంది, ఎప్పుడూ 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-looking string గా మారుస్తుంది. ఇద్దరు users ఒకే password కలిగి ఉన్నా, BCrypt ప్రతిసారి random "salt" జోడిస్తుంది కాబట్టి different hashes ఉత్పత్తి చేస్తుంది.
@Configuration
public class PasswordConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12); // 12 rounds of hashing
}
}
// BCrypt ఏమి చేస్తుంది:
// "mypassword123" -> "$2a$12$LJ3m4ys8Hp5Rq.Xz9vKnet..."
// అదే password మళ్ళీ -> "$2a$12$R8pQw7Yt.N2mFkA5dB3kZu..." (different hash!)
User log in అయినప్పుడు, BCrypt stored hash ను "decode" చేయదు. బదులుగా, ఇది submitted password ను అదే విధంగా hash చేసి ఫలితాలను compare చేస్తుంది. అవి match అయితే, password correct.
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()) // DB నుండి BCrypt hash
.roles(user.getRole()) // "USER" 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;
// కొత్త ticket print చేయడం
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();
}
// Refresh token generate చేయడం (ఎక్కువ కాలం ఉంటుంది)
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();
}
// Ticket నుండి username చదవడం
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
// Ticket ఇంకా valid గా ఉందా check చేయడం
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 పై run అవుతుంది. ఇది check చేస్తుంది: ఈ request కి valid JWT ఉందా? ఉంటే, ఇది Spring Security కి "ఈ వ్యక్తి authenticated" అని చెప్తుంది. లేకపోతే, ఏమీ చేయకుండా తదుపరి filter handle చేయనిస్తుంది.
@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. Authorization header తీసుకోండి
String authHeader = request.getHeader("Authorization");
// 2. Token లేదా? Request ను continue కానివ్వండి (public endpoint కావచ్చు)
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
// 3. Token extract చేయండి ("Bearer " prefix తీసేయండి)
String token = authHeader.substring(7);
String username = jwtUtil.extractUsername(token);
// 4. Username వచ్చింది మరియు ఇంకా ఎవరూ authenticated కాకపోతే...
if (username != null &&
SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
// 5. Token validate చేయండి
if (jwtUtil.isTokenValid(token, userDetails)) {
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authToken.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request));
// 6. Spring Security కి చెప్పండి: ఈ user 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;
// అన్ని dependencies కోసం constructor injection
@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 username + password verify చేస్తుంది
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 అవసరం, మరియు JWT filter ను chain లో ఎక్కడ పెట్టాలి.
@Configuration
@EnableWebSecurity
@EnableMethodSecurity // @PreAuthorize మరియు @Secured enable చేస్తుంది
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
// Stateless JWT APIs కోసం CSRF disable చేయండి (session లేదు = CSRF risk లేదు)
.csrf(csrf -> csrf.disable())
// CORS configure చేయండి
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
// Session management ను stateless గా set చేయండి (server-side sessions లేవు)
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// ఏ endpoints public vs protected అని define చేయండి
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll() // login, register
.requestMatchers("/api/public/**").permitAll() // public content
.requestMatchers("/api/admin/**").hasRole("ADMIN") // admin only
.anyRequest().authenticated() // మిగతా అన్నింటికీ auth అవసరం
)
// Authentication provider set చేయండి
.authenticationProvider(authenticationProvider())
// Spring యొక్క username/password filter ముందు JWT filter add చేయండి
.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 level లో access control చేస్తుంది. కానీ కొన్నిసార్లు మీకు finer control అవసరం — "admins మాత్రమే delete చేయగలరు, కానీ authenticated user ఎవరైనా read చేయగలరు" అని చెప్పడం లాంటిది. అక్కడే @PreAuthorize మరియు @Secured వస్తాయి.
@RestController
@RequestMapping("/api/posts")
public class BlogPostController {
private final BlogPostService postService;
// Authenticated ఎవరైనా read చేయగలరు
@GetMapping
public List<BlogPost> getAllPosts() {
return postService.findAll();
}
// Post author లేదా admins మాత్రమే 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);
}
// Admins మాత్రమే delete చేయగలరు
@PreAuthorize("hasRole('ADMIN')")
@DeleteMapping("/{postId}")
public void deletePost(@PathVariable Long postId) {
postService.delete(postId);
}
// Moderators మరియు admins మాత్రమే
@Secured({"ROLE_MODERATOR", "ROLE_ADMIN"})
@PutMapping("/{postId}/approve")
public BlogPost approvePost(@PathVariable Long postId) {
return postService.approve(postId);
}
// SpEL expression: user వారి స్వంత 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 access చేయగలదు. @Secured సరళమైనది — ఇది roles మాత్రమే check చేస్తుంది. Expressions అవసరమైనప్పుడు @PreAuthorize ఉపయోగించండి, role checks మాత్రమే అవసరమైనప్పుడు @Secured ఉపయోగించండి.
Role-Based Access Control (RBAC): Permissions యొక్క Hierarchy
RBAC ఒక office building లో key card system లాంటిది. Intern card front door తెరుస్తుంది. Manager card front door plus meeting rooms తెరుస్తుంది. CEO card అన్నీ తెరుస్తుంది. ప్రతి role permissions set ఇస్తుంది, మరియు higher roles lower roles యొక్క అన్ని permissions include చేస్తాయి.
// Roles ను enum గా define చేయండి
public enum AppRole {
USER, // posts read చేయగలరు, comments రాయగలరు
MODERATOR, // USER + comments approve/reject చేయగలరు
ADMIN // MODERATOR + posts delete చేయగలరు, users manage చేయగలరు
}
// Role hierarchy configuration
@Bean
public RoleHierarchy roleHierarchy() {
return RoleHierarchyImpl.withDefaultRolePrefix()
.role("ADMIN").implies("MODERATOR")
.role("MODERATOR").implies("USER")
.build();
}
// ఇప్పుడు @PreAuthorize("hasRole('USER')") MODERATOR మరియు ADMIN ను కూడా allow చేస్తుంది
CORS: మీ API తో ఎవరు మాట్లాడగలరు?
మీ API ఒక private phone line అని ఊహించుకోండి. CORS (Cross-Origin Resource Sharing) caller ID system — ఇది call ఎక్కడ నుండి వస్తుందో check చేసి, pick up చేయాలా వద్దా అని decide చేస్తుంది.
CORS configuration లేకుండా, browsers different domains నుండి requests block చేస్తాయి. localhost:3000 పై మీ React app, మీరు explicitly allow చేయకపోతే localhost:8080 పై మీ Spring API తో communicate చేయలేదు.
// Option 1: SecurityConfig లో Global CORS (పైన చూపించబడింది)
// Option 2: Per-controller CORS
@CrossOrigin(origins = "http://localhost:3000", maxAge = 3600)
@RestController
@RequestMapping("/api/posts")
public class BlogPostController {
// ఈ controller లో అన్ని endpoints localhost:3000 ను allow చేస్తాయి
}
// 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 అనేది ఎవరైనా మీ pocket లో signed check దొంగతనంగా పెట్టడం లాంటిది. మీరు ఒక malicious website visit చేస్తారు, మరియు అది మీ existing session cookies ఉపయోగించి మీ bank కి secretly request పంపుతుంది. Cookies valid కాబట్టి bank మీరేనని అనుకుంటుంది.
JWT-based APIs కోసం, CSRF concern కాదు ఎందుకంటే మనం authentication కోసం cookies ఉపయోగించము. JWT Authorization header లో పంపబడుతుంది, మరియు cookies తో చేసినట్లు browsers automatically headers attach చేయవు. అందుకే మన config లో CSRF disable చేస్తాము: .csrf(csrf -> csrf.disable()).
Session-based apps కోసం (Thymeleaf తో server-rendered pages), CSRF protection critical:
// Session-based apps కోసం CSRF enabled ఉంచండి
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler()))
// మీ Thymeleaf form లో, Spring token ను automatically add చేస్తుంది:
// <form th:action="@{/api/posts}" method="post">
// <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
// ...
// </form>
OAuth2 Basics: Google మరియు GitHub Login Handle చేయనివ్వండి
OAuth2 అనేది hotel లో check in చేయడానికి మీ driving license ఉపయోగించడం లాంటిది. Hotel మీకు ID issue చేయదు — వారు మీ license issue చేసిన government (Google, GitHub) ను trust చేస్తారు. మీరు trusted third party ద్వారా మీరు ఎవరో prove చేస్తారు.
Spring Security OAuth2 login ను దాదాపు trivial గా చేస్తుంది:
// 1. Dependency add చేయండి
// <dependency>
// <groupId>org.springframework.boot</groupId>
// <artifactId>spring-boot-starter-oauth2-client</artifactId>
// </dependency>
// 2. application.yml లో configure చేయండి
// 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. SecurityFilterChain update చేయండి
@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 code ను user info కోసం exchange చేస్తుంది. Spring steps 2-4 ను automatically handle చేస్తుంది.
పూర్తి 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 # production లో create/update ఎప్పుడూ ఉపయోగించకండి
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
# Security events logging
logging:
level:
org.springframework.security: DEBUG # production లో దీన్ని off చేయండి
com.example.security: INFO
Production Security Checklist
- ప్రతిచోటా HTTPS ఉపయోగించండి (plain HTTP పై ఎప్పుడూ JWTs పంపకండి)
- Short access token expiration set చేయండి (15-60 minutes)
- Refresh tokens ను securely store చేయండి (httpOnly cookies లేదా encrypted database)
- Strong, random JWT secret ఉపయోగించండి (కనీసం 256 bits)
- Password hashing కోసం BCrypt 10-12 rounds
- 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 అంటే మీరు ఎవరో prove చేయడం — airport లో మీ ID చూపించడం లాంటిది. Authorization అంటే మీరు ఏమి చేయడానికి అనుమతించబడ్డారో prove చేయడం — మీ boarding pass flight 42 board చేయమని చెప్తుంది కానీ flight 99 కాదు. Spring Security ముందు authentication handle చేస్తుంది (UserDetailsService మరియు AuthenticationManager ద్వారా), తర్వాత authorization (SecurityFilterChain rules మరియు @PreAuthorize ద్వారా). Authorized కావడానికి ముందు మీరు authenticated అయి ఉండాలి.
2. JWT-based APIs కోసం CSRF ఎందుకు disable చేస్తాము?
CSRF attacks మీ browser ను requests తో automatically cookies పంపేలా trick చేయడం ద్వారా పనిచేస్తాయి. JWT tokens memory లో store అవుతాయి (cookies కాదు) మరియు Authorization header లో manually పంపబడతాయి కాబట్టి, browser వాటిని పంపేలా trick కాదు. Automatic cookies లేవు అంటే CSRF risk లేదు. అయితే, మీరు JWTs ను cookies లో store చేస్తే (కొన్ని applications చేస్తాయి), మీరు CSRF protection ను తప్పనిసరిగా enabled గా ఉంచాలి.
3. Access token expire అయినప్పుడు ఏమి జరుగుతుంది?
Access token expire అయినప్పుడు, server 401 Unauthorized response return చేస్తుంది. Client తర్వాత కొత్త access token పొందడానికి refresh token ను /api/auth/refresh endpoint కి పంపాలి — user ని మళ్ళీ login అవమని అడగకుండా. ఇలా ఆలోచించండి: access token theme park లో day pass (short-lived), మరియు refresh token మీ season pass (long-lived). మీ day pass expire అయినప్పుడు, కొత్త day pass పొందడానికి మీ season pass చూపిస్తారు.
4. @PreAuthorize లేదా @Secured ఉపయోగించాలా?
చాలా cases లో @PreAuthorize ఉపయోగించండి ఎందుకంటే ఇది SpEL expressions support చేస్తుంది, ఇది "ఈ post author మాత్రమే edit చేయగలరు" (@PreAuthorize("#postId == authentication.principal.id")) వంటి conditions రాయడానికి అనుమతిస్తుంది. సాధారణ role checks మాత్రమే అవసరమైనప్పుడు మరియు annotation ఎక్కువ readable గా ఉండాలనుకున్నప్పుడు @Secured ఉపయోగించండి. @Secured లో Spring Expression Language mix చేయలేరు — ఇది role names మాత్రమే accept చేస్తుంది.
5. Password hashing కోసం BCrypt SHA-256 కంటే ఎలా భిన్నంగా ఉంటుంది?
SHA-256 general-purpose hash — ఇది design ప్రకారం fast, అంటే attackers second కి billions of passwords try చేయగలరు. BCrypt ఉద్దేశపూర్వకంగా slow. ఇది passwords కోసం ప్రత్యేకంగా design చేయబడింది మరియు ప్రతి hash ఎక్కువ సమయం తీసుకునేలా చేసే "cost factor" (rounds సంఖ్య) include చేస్తుంది. 12 rounds తో BCrypt, billion passwords try చేయడానికి attacker కి seconds బదులు centuries పడుతుంది. BCrypt ప్రతి hash కి automatically random "salt" కూడా add చేస్తుంది, కాబట్టి ఒకే password ఉన్న ఇద్దరు users different hashes పొందుతారు.