Spring Boot OAuth2 ආරක්ෂාව | Spring Boot Security Guide SC

කොහොමද යාලුවනේ! අද අපි කතා කරන්න යන්නේ ඔයාලා ගොඩක් දෙනෙක්ට වැදගත් වෙන, Software Engineering ලෝකේ නැතුවම බැරි security concept එකක් ගැන – ඒ තමයි OAuth2. විශේෂයෙන්ම, මේක අපේ Spring Boot applications වලට කොහොමද දාගන්නේ කියලා අපි පියවරෙන් පියවර බලමු.
දැන් හිතන්නකෝ, ඔයා app එකක් හදනවා, ඒකෙන් user ලට තමන්ගේ account එකට login වෙලා තොරතුරු බලන්න, වෙනස් කරන්න පුළුවන්. හැබැයි, මේ තොරතුරු ආරක්ෂා කරන්න ඕනේ නේද? සාමාන්යයෙන් අපි username password දාලා login වෙනවා. ඒක හොඳයි, හැබැයි දැන් අපි කතා කරන්නේ අද කාලේ, third-party apps වලටත් අපේ user data වලට access දෙන්න ඕන වෙන වෙලාවල් ගැන. උදාහරණයක් විදියට, Google account එකෙන් වෙන app එකකට login වෙන එක, Facebook එකෙන් game එකකට login වෙන එක. මේ හැමදේම පිටිපස්සේ ඉන්නේ මේ OAuth2 තමයි.
හරි, එහෙනම් වැඩේට බහිමු! අද අපි මේ "කතාව" ඉගෙන ගමු, අන්තිමට ඔයාලට ඔයාලගේම Spring Boot app එකට OAuth2 දාගන්න පුළුවන් වෙයි කියලා මම හිතනවා.
OAuth2 කියන්නේ මොකක්ද?
සරලවම කිව්වොත්, OAuth2 කියන්නේ authorization framework එකක්. "Authorization" කියන්නේ මොකක්ද? "මේ user ට මේ දේ කරන්න පුළුවන්" කියලා අනුමැතිය දෙන එක. "Authentication" කියන්නේ "මේ user අහවලා" කියලා තහවුරු කරන එක. මේ දෙක පටලවා ගන්න එපා.
මේක තේරුම් ගන්න මම පොඩි උදාහරණයක් කියන්නම්. හිතන්න, ඔයාගේ ගෙදර යතුර ඔයාගේ ළඟ තියනවා. ඒකෙන් ඔයාට ගෙදරට ඇතුල් වෙන්න පුළුවන් (Authentication). දැන් ඔයාට ඕනේ ඔයාගේ යාලුවෙක්ට ඔයාගේ ගෙදරට ගිහින් පොත් ටිකක් අරන් එන්න. හැබැයි ඔයාට ගෙදර යතුරම දෙන්න බෑ, මොකද එතකොට එයාට හැමදේම කරන්න පුළුවන් වෙනවානේ. ඒ වෙනුවට, ඔයා යාලුවාට පොඩි චිට් එකක් දෙනවා, "මේ යාලුවාට මගේ ගෙදරින් පොත් ටික ගන්න අවසර දීලා තියනවා" කියලා. යාලුවා ගේ ළඟ ඉන්න ආරක්ෂකයාට (security guard) චිට් එක පෙන්නනවා, ආරක්ෂකයා ඒක බලලා යාලුවාට පොත් ටික ගන්න විතරක් ඉඩ දෙනවා. ගෙදර හැමදේම කරන්න බෑ.
මේ කතාවේ:
- ඔයා තමයි Resource Owner (resource එකේ අයිතිකාරයා).
- ගෙදර තමයි Resource Server (ආරක්ෂා කරන resource තියන server එක).
- යාලුවා තමයි Client (resource access කරන්න හදන app එක).
- ආරක්ෂකයා තමයි Authorization Server (අනුමැතිය දෙන server එක).
- චිට් එක තමයි Access Token (අවසරය තහවුරු කරන ටෝකනය).
OAuth2 වැඩ කරන්නේ හරියටම මේ විදියට. Client app එකකට (අපේ යාලුවා) user ගේ (ඔයාගේ) data (පොත්) වලට access දෙන්නේ user ගේ username password නොදී, ඒ වෙනුවට Access Token එකක් දීලා. මේ Token එකෙන් කියන්නේ "මේ Client ට මේ user ගේ මේ resource වලට මේ මේ දේවල් කරන්න පුළුවන්" කියලා. ඒකත් සීමා සහිතයි.
OAuth2 වල ප්රධාන flows කීපයක් තියනවා. ඒ අතරින් වැඩිපුරම භාවිත වෙන්නේ සහ ආරක්ෂිතම එක තමයි Authorization Code Flow එක. අද අපි කතා කරන Spring Boot Resource Server එකේදී මේ Token validation එක තමයි අපි කරන්න යන්නේ.
Spring Boot එකට OAuth2 ගේමු!
හරි, දැන් අපි අපේ Spring Boot Project එකට මේක කොහොමද ගේන්නේ කියලා බලමු. මේක කරන්න අපිට පුංචි Project එකක් ඕනේ. ඔයාට පුළුවන් Spring Initializr (start.spring.io) එකට ගිහින් අලුත් Project එකක් හදාගන්න. Dependencies විදියට මේ ටික එකතු කරගන්න:
- Spring Web (REST APIs හදන්න)
- Spring Security (ආරක්ෂාවට)
- Spring Boot Starter OAuth2 Resource Server (OAuth2 tokens validate කරන්න)
Project එක download කරලා, ඔයාගේ IDE (IntelliJ IDEA, VS Code, Eclipse වගේ එකක) එකක open කරගන්න.
Dependencies එකතු කරගමු
pom.xml (Maven) එකේ මේ dependencies තියනවාද කියලා බලන්න. නැත්තම් එකතු කරගන්න:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
</dependencies>
Gradle නම්, build.gradle එකේ මේ ටික:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
}
Resource Server Setup (APIs ආරක්ෂා කරමු!)
අපේ Spring Boot application එක දැන් Resource Server එකක් විදියට වැඩ කරන්නේ, ඒ කියන්නේ එයාගේ APIs ආරක්ෂා කරනවා. Client කෙනෙක් Token එකක් අරන් ආවම, මේ Resource Server එක Token එක validate කරලා, ඒ Token එකෙන් අනුමැතිය තියනවා නම් විතරක් API එකට access දෙනවා.
Basic REST Controller එකක් හදමු
මුලින්ම අපි ආරක්ෂා කරන්න හදන API එකක් හදමු. මේක හරිම සරලයි:
// src/main/java/com/example/oauth2demo/controller/MessageController.java
package com.example.oauth2demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MessageController {
@GetMapping("/hello")
public String hello() {
return "Hello from a secured endpoint!";
}
@GetMapping("/public")
public String publicEndpoint() {
return "This is a public endpoint, no authentication needed.";
}
}
දැන් මේ Project එක Run කරොත්, Spring Security නිසා `/hello` endpoint එකට access කරන්න username password ඉල්ලයි. අපිට ඕනේ ඒකට OAuth2 token එකක් දීලා access කරන්න.
Security Configuration ලියමු
අපේ Resource Server එකට කියන්න ඕනේ "ඔයා OAuth2 JWT tokens validate කරන්න" කියලා. ඒකට අපි `SecurityFilterChain` එකක් customize කරන්න ඕනේ.
// src/main/java/com/example/oauth2demo/config/SecurityConfig.java
package com.example.oauth2demo.config;
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.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/public").permitAll() // Public endpoint
.anyRequest().authenticated() // All other requests need authentication
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> {}) // Configure OAuth2 Resource Server to use JWTs
);
return http.build();
}
}
මේ කෝඩ් එකෙන් කියන්නේ:
- `/public` කියන endpoint එකට හැමෝටම access දෙන්න.
- අනිත් හැම endpoint එකකටම Authentication එකක් ඕනේ.
- ඒ Authentication එක කරන්න OAuth2 Resource Server එකක් විදියට JWT tokens (JSON Web Tokens) validate කරන්න.
application.properties file එක සකස් කරමු
අපේ Resource Server එකට කියන්න ඕනේ "JWT Token issue කරන්නේ කවුද?" කියලා. මේක Issuer URI එක. මේක සාමාන්යයෙන් Authorization Server එකේ URL එක. අපි Keycloak, Auth0 වගේ service එකක් භාවිත කරනවා නම්, ඒකෙන් අපිට මේ URI එක දෙනවා. උදාහරණයක් විදියට, Keycloak Local එකේ Run කරනවා නම්, මේ වගේ URI එකක් වෙන්න පුළුවන්:
# src/main/resources/application.properties
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/your-realm-name
your-realm-name
කියන තැනට ඔයාගේ Keycloak realm එකේ නම දෙන්න. මේ URI එකෙන් තමයි Spring Boot JWT token එකේ signature එක validate කරන්නේ, ඒ වගේම token එක ඇතුලේ තියන claims load කරගන්නේ.
Test කරමු!
දැන් Project එක Run කරන්න. (Main Application Class එක Run කරන්න.)
දැන් අපිට අවශ්ය වෙනවා Valid JWT Access Token එකක්. මේක ගන්න පුළුවන් Authorization Server එකකින්. (Keycloak, Auth0, Okta වගේ එකකින්.)
Keycloak භාවිත කරනවා නම් (Quick Guide):
- Keycloak Download කරලා Run කරන්න.
- New Realm එකක් හදන්න (e.g., `myrealm`).
- Client එකක් හදන්න (e.g., `my-client`, Access Type: `public` හෝ `confidential`, Standard Flow Enabled: `ON`).
- User කෙනෙක් හදන්න (e.g., `testuser`, password `password`).
- Response එකේ `access_token` එක copy කරගන්න.
පහත curl command එකෙන් access token එකක් ගන්න:
curl -X POST "http://localhost:8080/realms/myrealm/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=my-client" \
-d "username=testuser" \
-d "password=password" \
-d "grant_type=password"
(Grant Type `password` කියන්නේ Development වලට විතරයි, Production වලට `authorization_code` flow එක භාවිත කරන්න.)
දැන් Postman, Insomnia වගේ tool එකක් අරන්:
- Method: `GET`
- URL: `http://localhost:8080/hello` (හෝ ඔයාගේ application run වෙන port එක)
- Headers:
- Key: `Authorization`
- Value: `Bearer YOUR_ACCESS_TOKEN_HERE` (
YOUR_ACCESS_TOKEN_HERE
තැනට උඩින් copy කරගත්තු token එක දාන්න.)
- Request එක send කරන්න.
ඔයාට "Hello from a secured endpoint!" කියලා message එක එනවා නම්, ඔයාගේ OAuth2 Resource Server එක සාර්ථකයි!
Token එකක් නැතුව හෝ වැරදි token එකක් එක්ක request කරොත්, `401 Unauthorized` හෝ `403 Forbidden` වගේ error එකක් එයි. ඒක තමයි අපි බලාපොරොත්තු වෙන්නේ!
Custom Authorization Logic (ටිකක් ඇඩ්වාන්ස් වෙමු!)
දැන් අපි බලමු මේ Token එක ඇතුලේ තියන scopes (අවසරයන්) හෝ roles (භූමිකාවන්) භාවිත කරලා APIs තවත් ආරක්ෂා කරගන්නේ කොහොමද කියලා.
උදාහරණයක් විදියට, "admin" role තියන user කෙනෙක්ට විතරක් access දෙන්න ඕනේ නම්, නැත්නම් "write" scope එක තියන user කෙනෙක්ට විතරක් data update කරන්න දෙන්න ඕනේ නම්, ඒක කරන්න පුළුවන්.
අපි මේකට Spring Security Expression Language එක භාවිත කරනවා, `@PreAuthorize` annotation එක එක්ක.
// src/main/java/com/example/oauth2demo/controller/MessageController.java (Updated)
package com.example.oauth2demo.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MessageController {
@GetMapping("/hello")
@PreAuthorize("hasAuthority('SCOPE_read')") // Requires 'read' scope
public String hello() {
return "Hello from a secured endpoint with read scope!";
}
@GetMapping("/admin")
@PreAuthorize("hasRole('ADMIN')") // Requires 'ADMIN' role
public String adminHello() {
return "Hello Admin!";
}
@GetMapping("/public")
public String publicEndpoint() {
return "This is a public endpoint, no authentication needed.";
}
}
මේක වැඩ කරන්න නම්, අපිට අපේ Security Configuration එකේ `@EnableMethodSecurity` annotation එක එකතු කරන්න ඕනේ:
// src/main/java/com/example/oauth2demo/config/SecurityConfig.java (Updated)
package com.example.oauth2demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; // New Import
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity // Enable method level security
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/public").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> {})
);
return http.build();
}
}
දැන් Keycloak වගේ Authorization Server එකකදී user ට `read` scope එකක් හෝ `ADMIN` role එකක් assign කරලා, අලුත් token එකක් අරන් request කරොත්, අදාළ endpoint එකට access ලැබෙයි. (Keycloak වලදී Scopes සහ Roles JWT token එකේ claims විදියට එනවා.)
සාමාන්යයෙන් Scopes JWT එකේ `scope` claim එකේ එන්නේ space-separated string එකක් විදියට. Roles `realm_access.roles` හෝ `resource_access.client_id.roles` වගේ claim එකක array එකක් විදියට එනවා. Spring Security ඒවා automatically detect කරලා `ROLE_` prefix එකක් එක්ක authority එකක් විදියට register කරනවා.
Client Application (අපිම Client කෙනෙක් හදමුද?)
අපි මෙච්චර වෙලා කතා කලේ Resource Server එකක් ගැනනේ. හැබැයි මේ Resource Server එකට request කරන්නේ Client Application එකකින්. අපි Spring Boot එකෙන් Client Application එකකුත් හදන්න පුළුවන්. ඒකට `spring-boot-starter-oauth2-client` dependency එක භාවිත කරන්න පුළුවන්.
Client Application එකේදී වෙන්නේ:
- User කෙනෙක් login කරන්න හදනවා.
- Client Application එක User ව Authorization Server එකට redirect කරනවා.
- User Authorization Server එකේ login වෙලා, Client Application එකට තමන්ගේ data access කරන්න අවසර දෙනවා.
- Authorization Server එක Client Application එකට Access Token එකක් (සමහරවිට Refresh Token, ID Token එක්ක) ලබා දෙනවා.
- Client Application එක මේ Access Token එක භාවිත කරලා Resource Server එකේ APIs වලට request කරනවා.
මේක ගොඩක් දුරට `application.properties` එකේ Client Registration විස්තර දීලා කරන්න පුළුවන්:
# Example for an OAuth2 client
spring.security.oauth2.client.registration.keycloak.client-id=my-client
spring.security.oauth2.client.registration.keycloak.client-secret=YOUR_CLIENT_SECRET
spring.security.oauth2.client.registration.keycloak.client-authentication-method=client_secret_post
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.redirect-uri={baseUrl}/login/oauth2/code/{registrationId}
spring.security.oauth2.client.registration.keycloak.scope=openid,profile,email,read
spring.security.oauth2.client.provider.keycloak.authorization-uri=http://localhost:8080/realms/myrealm/protocol/openid-connect/auth
spring.security.oauth2.client.provider.keycloak.token-uri=http://localhost:8080/realms/myrealm/protocol/openid-connect/token
spring.security.oauth2.client.provider.keycloak.user-info-uri=http://localhost:8080/realms/myrealm/protocol/openid-connect/userinfo
spring.security.oauth2.client.provider.keycloak.jwk-set-uri=http://localhost:8080/realms/myrealm/protocol/openid-connect/certs
spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8080/realms/myrealm
මේ Client Application එකක් හදන එක තව පොඩ්ඩක් Complex. හැබැයි Spring Boot Starter OAuth2 Client එකෙන් මේක හරිම පහසුවෙන් කරන්න පුළුවන්. මේ ලිපියේ focus එක Resource Server එක නිසා අපි ඒක ගැන ගැඹුරට කතා කලේ නෑ. ඒත් ඔයාලට පුළුවන් මේ dependency එකත් එක්ක ඒ ගැන තවදුරටත් හොයලා බලන්න. ඊළඟ article එකකදී අපි Client Application එකක් හදන හැටි ගැන කතා කරමු.
අවසානයට...
ඉතින් යාලුවනේ, අද අපි Spring Boot Application එකකට OAuth2 Resource Server එකක් කොහොමද implement කරන්නේ කියලා ඉගෙන ගත්තා. මේක අද තියන applications වලට අතිශයින්ම වැදගත් security layer එකක්. Microservices architecture එකක් වගේ දේවල් වලදී මේ OAuth2 තමයි බහුලවම භාවිත වෙන්නේ.
Spring Boot වලින් මේ වගේ සංකීර්ණ security mechanisms implement කරන එක කොච්චර ලේසිද කියලා ඔයාලට තේරෙන්න ඇති. Dependencies ටිකක් දාගෙන, configuration ටිකක් කරගත්තම වැඩේ ගොඩ. හැබැයි production environment එකකට යනකොට මේ හැමදේම හොඳට තේරුම් අරන් කරන්න ඕනේ. Security කියන්නේ කිසිසේත්ම සැහැල්ලුවට ගන්න බැරි දෙයක්.
මේ article එක ඔයාලට ප්රයෝජනවත් වෙන්න ඇති කියලා මම හිතනවා. අනිවාර්යයෙන්ම මේ කෝඩ් ටික ඔයාලගේ PC එකේ Run කරලා බලන්න. මොකද, theory විතරක් මදි, practice එක තමයි වැදගත්ම. මොකක් හරි ප්රශ්නයක් ආවොත්, පහළින් comments section එකේ අහන්න. ඔයාලට මේ ගැන අලුත් Ideas තියනවනම්, experience තියනවනම් ඒවත් share කරන්න. හැමදාම වගේ, අපි හැමෝම එකතු වෙලා ඉගෙන ගමු.
එහෙනම්, තව අලුත් දෙයක් එක්ක හම්බවෙමු! සුභ දවසක්!