Skip to content

Commit

Permalink
Merge pull request #27 from kursph/feat/jwt-security
Browse files Browse the repository at this point in the history
Feat/jwt security
  • Loading branch information
kursph authored Nov 17, 2023
2 parents 00a42a2 + 262edee commit c49d9a0
Show file tree
Hide file tree
Showing 38 changed files with 871 additions and 102 deletions.
41 changes: 41 additions & 0 deletions backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<docker.username>kurshjku</docker.username>
<docker.image.name>kursph-spring-boot-api</docker.image.name>
<docker.image.tag/>
<jwt.version>0.12.3</jwt.version>
</properties>
<dependencies>
<dependency>
Expand Down Expand Up @@ -80,6 +81,46 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
</dependency>

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>${jwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>${jwt.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
<version>${jwt.version}</version>
<scope>runtime</scope>
</dependency>
<!-- Uncomment this next dependency if you are using:
- JDK 10 or earlier, and you want to use RSASSA-PSS (PS256, PS384, PS512) signature algorithms.
- JDK 10 or earlier, and you want to use EdECDH (X25519 or X448) Elliptic Curve Diffie-Hellman encryption.
- JDK 14 or earlier, and you want to use EdDSA (Ed25519 or Ed448) Elliptic Curve signature algorithms.
It is unnecessary for these algorithms on JDK 15 or later.
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId> or bcprov-jdk15to18 on JDK 7
<version>1.76</version>
<scope>runtime</scope>
</dependency>
-->

</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.kursph.auth;

import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("api/v1/auth")
public class AuthenticationController {
private final AuthenticationService authenticationService;

public AuthenticationController(AuthenticationService authenticationService) {
this.authenticationService = authenticationService;
}

@PostMapping("login")
public ResponseEntity<?> login(@RequestBody AuthenticationRequest authenticationRequest) {
AuthenticationResponse authenticationResponse = authenticationService.login(authenticationRequest);

return ResponseEntity
.ok()
.header(HttpHeaders.AUTHORIZATION, authenticationResponse.token())
.body(authenticationResponse);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.kursph.auth;

public record AuthenticationRequest(
String username,
String password
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.kursph.auth;

import com.kursph.customer.CustomerDTO;

public record AuthenticationResponse(
String token,
CustomerDTO customerDTO
) {
}
37 changes: 37 additions & 0 deletions backend/src/main/java/com/kursph/auth/AuthenticationService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.kursph.auth;

import com.kursph.customer.Customer;
import com.kursph.customer.CustomerDTO;
import com.kursph.customer.CustomerDTOMapper;
import com.kursph.jwt.JWTUtil;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

@Service
public class AuthenticationService {
private final AuthenticationManager authenticationManager;
private final CustomerDTOMapper customerDTOMapper;
private final JWTUtil jwtUtil;

public AuthenticationService(AuthenticationManager authenticationManager, CustomerDTOMapper customerDTOMapper, JWTUtil jwtUtil) {
this.authenticationManager = authenticationManager;
this.customerDTOMapper = customerDTOMapper;
this.jwtUtil = jwtUtil;
}

public AuthenticationResponse login(AuthenticationRequest authenticationRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
authenticationRequest.username(),
authenticationRequest.password()
)
);
Customer principal = (Customer) authentication.getPrincipal();
CustomerDTO customerDTO = customerDTOMapper.apply(principal);
String token = jwtUtil.issueToken(customerDTO.username(), customerDTO.roles());

return new AuthenticationResponse(token, customerDTO);
}
}
26 changes: 0 additions & 26 deletions backend/src/main/java/com/kursph/config/WebMvcConfig.java

This file was deleted.

60 changes: 56 additions & 4 deletions backend/src/main/java/com/kursph/customer/Customer.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.kursph.customer;

import jakarta.persistence.*;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.List;
import java.util.Objects;

@Entity
Expand All @@ -14,7 +19,7 @@
)
}
)
public class Customer {
public class Customer implements UserDetails {
@Id
@SequenceGenerator(
name = "customer_id_seq",
Expand Down Expand Up @@ -42,20 +47,32 @@ public class Customer {
nullable = false
)
private String gender;

public Customer(Integer id, String name, String email, Integer age, String gender) {
@Column(
nullable = false
)
private String password;

public Customer(
Integer id,
String password,
String name,
String email,
Integer age,
String gender) {
this.id = id;
this.name = name;
this.email = email;
this.age = age;
this.gender = gender;
this.password = password;
}

public Customer(String name, String email, Integer age, String gender) {
public Customer(String name, String email, String password, Integer age, String gender) {
this.name = name;
this.email = email;
this.age = age;
this.gender = gender;
this.password = password;
}

public Customer() {
Expand Down Expand Up @@ -125,4 +142,39 @@ public String toString() {
", gender='" + gender + '\'' +
'}';
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(new SimpleGrantedAuthority("ROLE_USER"));
}

@Override
public String getPassword() {
return this.password;
}

@Override
public String getUsername() {
return this.email;
}

@Override
public boolean isAccountNonExpired() {
return true;
}

@Override
public boolean isAccountNonLocked() {
return true;
}

@Override
public boolean isCredentialsNonExpired() {
return true;
}

@Override
public boolean isEnabled() {
return true;
}
}
18 changes: 14 additions & 4 deletions backend/src/main/java/com/kursph/customer/CustomerController.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.kursph.customer;

import com.kursph.jwt.JWTUtil;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
Expand All @@ -8,24 +11,31 @@
@RequestMapping("api/v1/customers")
public class CustomerController {
private final CustomerService customerService;
private final JWTUtil jwtUtil;

public CustomerController(CustomerService customerService) {
public CustomerController(CustomerService customerService, JWTUtil jwtUtil) {
this.customerService = customerService;
this.jwtUtil = jwtUtil;
}

@GetMapping
public List<Customer> getCustomers() {
public List<CustomerDTO> getCustomers() {
return customerService.getAllCustomers();
}

@GetMapping("{id}")
public Customer getCustomer(@PathVariable("id") Integer id) {
public CustomerDTO getCustomer(@PathVariable("id") Integer id) {
return customerService.getCustomer(id);
}

@PostMapping
public void registerCustomer(@RequestBody CustomerRegistrationRequest customerRegistrationRequest) {
public ResponseEntity<?> registerCustomer(@RequestBody CustomerRegistrationRequest customerRegistrationRequest) {
customerService.addCustomer(customerRegistrationRequest);
String jwt = jwtUtil.issueToken(customerRegistrationRequest.email(), "ROLE_USER");

return ResponseEntity.ok()
.header(HttpHeaders.AUTHORIZATION, jwt)
.build();
}

@DeleteMapping("{id}")
Expand Down
1 change: 1 addition & 0 deletions backend/src/main/java/com/kursph/customer/CustomerDAO.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ public interface CustomerDAO {
boolean existsPersonWithEmail(String email);
void removeCustomer(Customer customer);
void updateCustomer(Customer customer);
Optional<Customer> selectUserByEmail(String email);
}
14 changes: 14 additions & 0 deletions backend/src/main/java/com/kursph/customer/CustomerDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.kursph.customer;

import java.util.List;

public record CustomerDTO(
Integer id,
String name,
String email,
Integer age,
String gender,
List<String> roles,
String username
) {
}
26 changes: 26 additions & 0 deletions backend/src/main/java/com/kursph/customer/CustomerDTOMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.kursph.customer;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;

import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class CustomerDTOMapper implements Function<Customer, CustomerDTO> {
@Override
public CustomerDTO apply(Customer customer) {
return new CustomerDTO(
customer.getId(),
customer.getName(),
customer.getEmail(),
customer.getAge(),
customer.getGender(),
customer.getAuthorities()
.stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList()),
customer.getUsername()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class CustomerDataAccessService implements CustomerDAO{

Customer customer1 = new Customer(
1,
"Alex",
"foobar", "Alex",
"[email protected]",
22,
"MALE"
Expand Down Expand Up @@ -53,4 +53,10 @@ public void removeCustomer(Customer customer) {
public void updateCustomer(Customer customer) {

}

@Override
public Optional<Customer> selectUserByEmail(String email) {
return customers.stream().filter(customer -> customer.getUsername().equals(email))
.findFirst();
}
}
Loading

0 comments on commit c49d9a0

Please sign in to comment.