Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,23 @@
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.3</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.3</version>
<scope>runtime</scope>
</dependency>
</dependencies>

<build>
Expand Down
36 changes: 33 additions & 3 deletions src/main/java/com/example/bankapp/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
package com.example.bankapp.config;

import com.example.bankapp.security.JwtAuthenticationFilter;
import com.example.bankapp.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
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;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
Expand All @@ -19,13 +25,38 @@ public class SecurityConfig {
@Autowired
AccountService accountService;

@Autowired
JwtAuthenticationFilter jwtAuthenticationFilter;

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

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}

@Bean
@Order(1)
public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher("/api/**")
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/**").authenticated()
)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

return http.build();
}

@Bean
@Order(2)
public SecurityFilterChain webSecurityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authz -> authz
Expand Down Expand Up @@ -55,6 +86,5 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(accountService).passwordEncoder(passwordEncoder());

}
}
}
73 changes: 73 additions & 0 deletions src/main/java/com/example/bankapp/controller/AuthController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.example.bankapp.controller;

import com.example.bankapp.dto.AuthResponse;
import com.example.bankapp.dto.LoginRequest;
import com.example.bankapp.dto.RegisterRequest;
import com.example.bankapp.model.Account;
import com.example.bankapp.security.JwtUtil;
import com.example.bankapp.service.AccountService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

private final AccountService accountService;
private final JwtUtil jwtUtil;
private final AuthenticationManager authenticationManager;

public AuthController(AccountService accountService, JwtUtil jwtUtil, AuthenticationManager authenticationManager) {
this.accountService = accountService;
this.jwtUtil = jwtUtil;
this.authenticationManager = authenticationManager;
}

@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())
);

UserDetails userDetails = accountService.loadUserByUsername(request.getUsername());
String token = jwtUtil.generateToken(userDetails);

AuthResponse response = new AuthResponse(token, request.getUsername(), "Login successful");
return ResponseEntity.ok(response);
} catch (BadCredentialsException e) {
Map<String, String> error = new HashMap<>();
error.put("error", "Invalid username or password");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(error);
} catch (Exception e) {
Map<String, String> error = new HashMap<>();
error.put("error", "Authentication failed: " + e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}

@PostMapping("/register")
public ResponseEntity<?> register(@RequestBody RegisterRequest request) {
try {
Account account = accountService.registerAccount(request.getUsername(), request.getPassword());

UserDetails userDetails = accountService.loadUserByUsername(account.getUsername());
String token = jwtUtil.generateToken(userDetails);

AuthResponse response = new AuthResponse(token, account.getUsername(), "Registration successful");
return ResponseEntity.status(HttpStatus.CREATED).body(response);
} catch (RuntimeException e) {
Map<String, String> error = new HashMap<>();
error.put("error", e.getMessage());
return ResponseEntity.badRequest().body(error);
}
}
}
110 changes: 110 additions & 0 deletions src/main/java/com/example/bankapp/controller/BankApiController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package com.example.bankapp.controller;

import com.example.bankapp.dto.*;
import com.example.bankapp.model.Account;
import com.example.bankapp.model.Transaction;
import com.example.bankapp.service.AccountService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api")
public class BankApiController {

private final AccountService accountService;

public BankApiController(AccountService accountService) {
this.accountService = accountService;
}

@GetMapping("/account")
public ResponseEntity<AccountDTO> getAccount() {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
Account account = accountService.findAccountByUsername(username);
AccountDTO accountDTO = new AccountDTO(account.getId(), account.getUsername(), account.getBalance());
return ResponseEntity.ok(accountDTO);
}

@PostMapping("/deposit")
public ResponseEntity<?> deposit(@RequestBody DepositRequest request) {
try {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
Account account = accountService.findAccountByUsername(username);
accountService.deposit(account, request.getAmount());

Account updatedAccount = accountService.findAccountByUsername(username);
AccountDTO accountDTO = new AccountDTO(updatedAccount.getId(), updatedAccount.getUsername(), updatedAccount.getBalance());

Map<String, Object> response = new HashMap<>();
response.put("message", "Deposit successful");
response.put("account", accountDTO);
return ResponseEntity.ok(response);
} catch (RuntimeException e) {
Map<String, String> error = new HashMap<>();
error.put("error", e.getMessage());
return ResponseEntity.badRequest().body(error);
}
}

@PostMapping("/withdraw")
public ResponseEntity<?> withdraw(@RequestBody WithdrawRequest request) {
try {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
Account account = accountService.findAccountByUsername(username);
accountService.withdraw(account, request.getAmount());

Account updatedAccount = accountService.findAccountByUsername(username);
AccountDTO accountDTO = new AccountDTO(updatedAccount.getId(), updatedAccount.getUsername(), updatedAccount.getBalance());

Map<String, Object> response = new HashMap<>();
response.put("message", "Withdrawal successful");
response.put("account", accountDTO);
return ResponseEntity.ok(response);
} catch (RuntimeException e) {
Map<String, String> error = new HashMap<>();
error.put("error", e.getMessage());
return ResponseEntity.badRequest().body(error);
}
}

@PostMapping("/transfer")
public ResponseEntity<?> transfer(@RequestBody TransferRequest request) {
try {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
Account fromAccount = accountService.findAccountByUsername(username);
accountService.transferAmount(fromAccount, request.getToUsername(), request.getAmount());

Account updatedAccount = accountService.findAccountByUsername(username);
AccountDTO accountDTO = new AccountDTO(updatedAccount.getId(), updatedAccount.getUsername(), updatedAccount.getBalance());

Map<String, Object> response = new HashMap<>();
response.put("message", "Transfer successful");
response.put("account", accountDTO);
return ResponseEntity.ok(response);
} catch (RuntimeException e) {
Map<String, String> error = new HashMap<>();
error.put("error", e.getMessage());
return ResponseEntity.badRequest().body(error);
}
}

@GetMapping("/transactions")
public ResponseEntity<List<TransactionDTO>> getTransactions() {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
Account account = accountService.findAccountByUsername(username);
List<Transaction> transactions = accountService.getTransactionHistory(account);

List<TransactionDTO> transactionDTOs = transactions.stream()
.map(t -> new TransactionDTO(t.getId(), t.getAmount(), t.getType(), t.getTimestamp()))
.collect(Collectors.toList());

return ResponseEntity.ok(transactionDTOs);
}
}
42 changes: 42 additions & 0 deletions src/main/java/com/example/bankapp/dto/AccountDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.example.bankapp.dto;

import java.math.BigDecimal;

public class AccountDTO {
private Long id;
private String username;
private BigDecimal balance;

public AccountDTO() {
}

public AccountDTO(Long id, String username, BigDecimal balance) {
this.id = id;
this.username = username;
this.balance = balance;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public BigDecimal getBalance() {
return balance;
}

public void setBalance(BigDecimal balance) {
this.balance = balance;
}
}
40 changes: 40 additions & 0 deletions src/main/java/com/example/bankapp/dto/AuthResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.example.bankapp.dto;

public class AuthResponse {
private String token;
private String username;
private String message;

public AuthResponse() {
}

public AuthResponse(String token, String username, String message) {
this.token = token;
this.username = username;
this.message = message;
}

public String getToken() {
return token;
}

public void setToken(String token) {
this.token = token;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}
}
22 changes: 22 additions & 0 deletions src/main/java/com/example/bankapp/dto/DepositRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.example.bankapp.dto;

import java.math.BigDecimal;

public class DepositRequest {
private BigDecimal amount;

public DepositRequest() {
}

public DepositRequest(BigDecimal amount) {
this.amount = amount;
}

public BigDecimal getAmount() {
return amount;
}

public void setAmount(BigDecimal amount) {
this.amount = amount;
}
}
Loading