This commit is contained in:
j-weissen 2023-12-12 12:49:23 +01:00
parent 74967d1cfa
commit f33d798663
8 changed files with 121 additions and 46 deletions

View file

@ -26,8 +26,9 @@
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>com.auth0</groupId>
<artifactId>spring-boot-starter-security</artifactId> <artifactId>java-jwt</artifactId>
<version>4.2.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.mysql</groupId> <groupId>com.mysql</groupId>

View file

@ -0,0 +1,4 @@
package me.jweissen.aeticket.dto.response;
public record LoginResponseDto(String token) {
}

View file

@ -4,21 +4,17 @@ import jakarta.persistence.*;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import lombok.Setter; import lombok.Setter;
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.List;
@Entity @Entity
@Table @Table
@Getter @Getter
@Setter @Setter
public class User implements UserDetails { public class User {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private int id; private Long id;
@Column(nullable = false) @Column(nullable = false)
@NonNull @NonNull
@ -36,45 +32,16 @@ public class User implements UserDetails {
@NonNull @NonNull
private String password; private String password;
@Column(nullable = false) @Column
@NonNull @NonNull
private Boolean admin; private String token;
@Column(nullable = false)
@Enumerated(EnumType.ORDINAL)
@NonNull
private Role role;
@OneToMany(mappedBy = "user") @OneToMany(mappedBy = "user")
@JoinColumn(nullable = false) @JoinColumn(nullable = false)
private List<Cart> carts; private List<Cart> carts;
@Enumerated(EnumType.STRING)
private Role role;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(new SimpleGrantedAuthority(role.name()));
}
@Override
public String getUsername() {
return 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;
}
}
} }

View file

@ -4,4 +4,5 @@ import me.jweissen.aeticket.model.User;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Integer> { public interface UserRepository extends JpaRepository<User, Integer> {
User findByEmail(String email);
} }

View file

@ -0,0 +1,35 @@
package me.jweissen.aeticket.service;
import me.jweissen.aeticket.dto.response.LoginResponseDto;
import me.jweissen.aeticket.model.User;
import me.jweissen.aeticket.repository.UserRepository;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class AuthService {
private final JwtService jwtService;
private final UserService userService;
private final UserRepository userRepository;
public AuthService(JwtService jwtService, UserService userService, UserRepository userRepository) {
this.jwtService = jwtService;
this.userService = userService;
this.userRepository = userRepository;
}
public Optional<LoginResponseDto> login(String username, String password) {
User user = userRepository.findByEmail(username);
// user not found or passwords don't match
if (user == null || !user.getPassword().equals(password)) {
return Optional.empty();
}
String token = jwtService.generateToken(user.getId());
user.setToken(token);
userRepository.save(user);
return Optional.of(new LoginResponseDto(username));
}
}

View file

@ -0,0 +1,57 @@
package me.jweissen.aeticket.service;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.Optional;
@Service
public class JwtService {
@Value("${token.secret}")
private String secret;
@Value("${token.validForHours}")
private Long tokenValidForHours;
private final JWTVerifier jwtVerifier;
private final Algorithm algorithm;
private final String userIdClaimKey = "userId";
private final Long tokenValidForMillis;
public JwtService() {
tokenValidForMillis = 1000L * 3600 * tokenValidForHours;
algorithm = Algorithm.HMAC256(secret);
jwtVerifier = JWT.require(algorithm).build();
}
public String generateToken(Long userId) {
return JWT.create()
.withSubject("aeticket user token")
.withClaim(userIdClaimKey , userId)
.withIssuedAt(new Date())
.withExpiresAt(new Date(System.currentTimeMillis() + tokenValidForMillis))
.sign(algorithm);
}
public Optional<Long> getUserId(String token) {
DecodedJWT decodedJWT;
try {
decodedJWT = jwtVerifier.verify(token);
} catch (JWTVerificationException e) {
// token not valid
return Optional.empty();
}
// token expired
if (decodedJWT.getExpiresAt().before(new Date())) {
return Optional.empty();
}
Claim claim = decodedJWT.getClaim(userIdClaimKey);
return Optional.of(claim.asLong());
}
}

View file

@ -10,9 +10,11 @@ import java.util.List;
@Service @Service
public class UserService { public class UserService {
private final UserRepository userRepository; private final UserRepository userRepository;
private final JwtService jwtService;
public UserService(UserRepository userRepository) { public UserService(UserRepository userRepository, JwtService jwtService) {
this.userRepository = userRepository; this.userRepository = userRepository;
this.jwtService = jwtService;
} }
public static UserResponseDto toDto(User user) { public static UserResponseDto toDto(User user) {

View file

@ -0,0 +1,8 @@
spring:
datasource:
username: my
password: ticket
url: 'jdbc:mysql://localhost:4306/myticket'
token:
secret: "RATP loves Laravel more than Symfony"
validForHours: 24