Skip to content

Commit a58caf2

Browse files
committed
Merge branch 'release/v1.0.0' into main
2 parents ad41e32 + 80aff04 commit a58caf2

File tree

23 files changed

+459
-117
lines changed

23 files changed

+459
-117
lines changed

server/pom.xml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
</parent>
1111
<groupId>br.com.it-neki</groupId>
1212
<artifactId>nekicard</artifactId>
13-
<version>0.0.2</version>
13+
<version>1.0.0</version>
1414
<name>nekicard</name>
1515
<description>Desafio NekiCard</description>
1616
<properties>
@@ -62,6 +62,20 @@
6262
<version>3.0.0</version>
6363
</dependency>
6464

65+
<dependency>
66+
<groupId>org.springframework.boot</groupId>
67+
<artifactId>spring-boot-starter-security</artifactId>
68+
</dependency>
69+
<dependency>
70+
<groupId>org.springframework.security</groupId>
71+
<artifactId>spring-security-test</artifactId>
72+
<scope>test</scope>
73+
</dependency>
74+
<dependency>
75+
<groupId>com.auth0</groupId>
76+
<artifactId>java-jwt</artifactId>
77+
<version>4.4.0</version>
78+
</dependency>
6579
</dependencies>
6680

6781
<build>

server/src/main/java/br/com/itneki/nekicard/card/controller/CardController.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
import io.swagger.v3.oas.annotations.media.Schema;
1010
import io.swagger.v3.oas.annotations.responses.ApiResponse;
1111
import io.swagger.v3.oas.annotations.responses.ApiResponses;
12+
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
1213
import io.swagger.v3.oas.annotations.tags.Tag;
14+
import jakarta.servlet.http.HttpServletRequest;
1315
import jakarta.transaction.Transactional;
1416
import jakarta.validation.Valid;
1517
import lombok.RequiredArgsConstructor;
@@ -22,7 +24,8 @@
2224
@RequestMapping("/card")
2325
@RestController
2426
@RequiredArgsConstructor
25-
@Tag(name="Cartão", description = "Informações e geração de cartões")
27+
@SecurityRequirement(name = "jwt_auth")
28+
@Tag(name="3.Cartão", description = "Informações e geração de cartões")
2629
public class CardController {
2730

2831
private final CardService cardService;
@@ -48,7 +51,7 @@ public ResponseEntity<Object> findById(@PathVariable UUID id){
4851
}
4952

5053
@Operation(summary = "Salva um novo cartão",
51-
description = "Essa função é responsável salvar um novo cartão e vinculá-lo ao usuário"
54+
description = "Essa função é responsável salvar um novo cartão e vinculá-lo ao usuário logado"
5255
)
5356
@ApiResponses({
5457
@ApiResponse(responseCode = "200", content = {
@@ -57,13 +60,15 @@ public ResponseEntity<Object> findById(@PathVariable UUID id){
5760
)
5861
}),
5962
})
60-
@PostMapping("/{userId}")
63+
@PostMapping
6164
@Transactional
62-
public ResponseEntity<Object> save(@PathVariable UUID userId,
65+
public ResponseEntity<Object> save(HttpServletRequest request,
6366
@RequestBody @Valid SaveCardDTO saveCardDTO,
6467
UriComponentsBuilder uriBuilder){
68+
69+
var userId = request.getAttribute("user_id").toString();
6570
try{
66-
var result = cardService.save(saveCardDTO, userId);
71+
var result = cardService.save(saveCardDTO, UUID.fromString(userId));
6772

6873
var uri = uriBuilder.path("/user/{id}")
6974
.buildAndExpand(result.getId())

server/src/main/java/br/com/itneki/nekicard/config/ModelMapperConfig.java

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package br.com.itneki.nekicard.config;
22

33
import br.com.itneki.nekicard.user.domain.User;
4-
import br.com.itneki.nekicard.user.dto.SignUpUserDTO;
5-
import br.com.itneki.nekicard.user.dto.UpdateUserDTO;
4+
import br.com.itneki.nekicard.security.SignUpDTO;
5+
import br.com.itneki.nekicard.user.dto.UserDetailsDTO;
66
import org.modelmapper.ModelMapper;
77
import org.springframework.context.annotation.Bean;
88
import org.springframework.context.annotation.Configuration;
@@ -12,11 +12,28 @@ public class ModelMapperConfig {
1212
@Bean
1313
public ModelMapper modelMapper() {
1414
var modelMapper = new ModelMapper();
15-
modelMapper.typeMap(SignUpUserDTO.class, User.class).addMappings(mapper -> {
16-
mapper.map(SignUpUserDTO::getNome, User::setName);
17-
mapper.map(SignUpUserDTO::getEmail, User::setEmail);
18-
mapper.map(SignUpUserDTO::getDataNascimento, User::setBirthdate);
19-
mapper.map(SignUpUserDTO::getSenha, User::setPassword);
15+
modelMapper.typeMap(SignUpDTO.class, User.class).addMappings(mapper -> {
16+
mapper.map(SignUpDTO::getNome, User::setName);
17+
mapper.map(SignUpDTO::getEmail, User::setEmail);
18+
mapper.map(SignUpDTO::getDataNascimento, User::setBirthdate);
19+
mapper.map(SignUpDTO::getSenha, User::setPassword);
20+
});
21+
22+
modelMapper.typeMap(User.class, UserDetailsDTO.class).addMappings(mapper -> {
23+
mapper.map(User::getId, UserDetailsDTO::setId);
24+
mapper.map(User::isStatus, UserDetailsDTO::setStatus);
25+
mapper.map(User::getName, UserDetailsDTO::setName);
26+
mapper.map(User::getEmail, UserDetailsDTO::setEmail);
27+
mapper.map(User::getBirthdate, UserDetailsDTO::setBirthdate);
28+
mapper.map(User::getProfilePhotoUrl, UserDetailsDTO::setProfilePhotoUrl);
29+
mapper.map(User::getLocality, UserDetailsDTO::setLocality);
30+
mapper.map(User::getDescription, UserDetailsDTO::setDescription);
31+
mapper.map(User::getWorkTime, UserDetailsDTO::setWorkTime);
32+
mapper.map(User::getWorkFunction, UserDetailsDTO::setWorkFunction);
33+
mapper.map(User::getSocialName, UserDetailsDTO::setSocialName);
34+
mapper.map(User::getPhone, UserDetailsDTO::setPhone);
35+
mapper.map(User::getCardList, UserDetailsDTO::setCardList);
36+
mapper.map(User::getSocialMediaList, UserDetailsDTO::setSocialMediaList);
2037
});
2138

2239
return modelMapper;

server/src/main/java/br/com/itneki/nekicard/config/SwaggerConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public OpenAPI openAPI(){
1313
return new OpenAPI()
1414
.info(new Info().title("NekiCard API Documentation")
1515
.description("Official documentation of NekiCard API Aplication")
16-
.version("0.0.2")
16+
.version("1.0.0")
1717
.contact(new Contact()
1818
.email("gabrieldamico22@gmail.com")
1919
.name("Gabriel Damico")
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package br.com.itneki.nekicard.exceptions;
2+
3+
public class AuthenticationException extends RuntimeException {
4+
public AuthenticationException(){
5+
super("Username/password incorrect");
6+
}
7+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package br.com.itneki.nekicard.security;
2+
3+
public record AuthResponse(String access_token, Long expires_at){
4+
5+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package br.com.itneki.nekicard.security;
2+
3+
import com.auth0.jwt.interfaces.DecodedJWT;
4+
import lombok.RequiredArgsConstructor;
5+
import org.springframework.beans.factory.annotation.Value;
6+
import org.springframework.security.crypto.password.PasswordEncoder;
7+
import org.springframework.stereotype.Service;
8+
import com.auth0.jwt.JWT;
9+
import com.auth0.jwt.algorithms.Algorithm;
10+
import com.auth0.jwt.exceptions.JWTVerificationException;
11+
12+
import java.time.Duration;
13+
import java.time.Instant;
14+
import java.util.List;
15+
16+
@Service
17+
@RequiredArgsConstructor
18+
public class JWTProvider {
19+
20+
private static String secretKey;
21+
22+
@Value("${security.token.secret}")
23+
public void setSecretKey(String secretKey) {
24+
JWTProvider.secretKey = secretKey;
25+
}
26+
public static AuthResponse generateToken(String id, String role) {
27+
Algorithm algorithm = Algorithm.HMAC256(secretKey);
28+
var expires_at = Instant.now().plus(Duration.ofHours(2));
29+
String token = JWT.create()
30+
.withIssuer("http://neki-card.com.br")
31+
.withSubject(id)
32+
.withClaim("role", List.of(role))
33+
.withExpiresAt(expires_at)
34+
.sign(algorithm);
35+
return new AuthResponse(token, expires_at.toEpochMilli());
36+
}
37+
38+
public static DecodedJWT validateToken(String token) {
39+
token = token.replace("Bearer ", "");
40+
Algorithm algorithm = Algorithm.HMAC256(secretKey);
41+
try {
42+
return JWT.require(algorithm).build().verify(token);
43+
} catch (JWTVerificationException ex) {
44+
throw new JWTVerificationException("Invalid Token");
45+
}
46+
}
47+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package br.com.itneki.nekicard.security;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.springframework.context.annotation.Bean;
5+
import org.springframework.context.annotation.Configuration;
6+
import org.springframework.http.HttpMethod;
7+
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
8+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
9+
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
10+
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
11+
import org.springframework.security.crypto.password.PasswordEncoder;
12+
import org.springframework.security.web.SecurityFilterChain;
13+
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
14+
@Configuration
15+
@EnableMethodSecurity
16+
@RequiredArgsConstructor
17+
public class SecurityConfig {
18+
private final SecurityUserFilter securityUserFilter;
19+
private static final String[] PERMIT_ALL_LIST = {
20+
"/auth/**",
21+
"/swagger-ui/**",
22+
"/v3/api-docs/**",
23+
"/swagger-resources/**"};
24+
@Bean
25+
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
26+
http.csrf(AbstractHttpConfigurer::disable)
27+
.authorizeHttpRequests(auth -> auth
28+
.requestMatchers(PERMIT_ALL_LIST).permitAll()
29+
.requestMatchers(HttpMethod.GET,"/user","/user/findAll").authenticated()
30+
.requestMatchers(HttpMethod.GET,"/user/{id}", "/image/**").permitAll()
31+
.anyRequest().authenticated()
32+
33+
)
34+
.addFilterBefore(securityUserFilter, BasicAuthenticationFilter.class);
35+
return http.build();
36+
}
37+
@Bean
38+
public PasswordEncoder passwordEncoder(){
39+
return new BCryptPasswordEncoder();
40+
}
41+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package br.com.itneki.nekicard.security;
2+
import jakarta.servlet.FilterChain;
3+
import jakarta.servlet.ServletException;
4+
import jakarta.servlet.http.HttpServletRequest;
5+
import jakarta.servlet.http.HttpServletResponse;
6+
import lombok.RequiredArgsConstructor;
7+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
8+
import org.springframework.security.core.authority.SimpleGrantedAuthority;
9+
import org.springframework.security.core.context.SecurityContextHolder;
10+
import org.springframework.stereotype.Component;
11+
import org.springframework.web.filter.OncePerRequestFilter;
12+
13+
import java.io.IOException;
14+
@Component
15+
@RequiredArgsConstructor
16+
public class SecurityUserFilter extends OncePerRequestFilter {
17+
@Override
18+
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
19+
String header = request.getHeader("Authorization");
20+
21+
if(header != null){
22+
var token = JWTProvider.validateToken(header);
23+
if(token == null){
24+
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
25+
return;
26+
}
27+
request.setAttribute("user_id", token.getSubject());
28+
29+
var roles = token.getClaim("role").asList(Object.class);
30+
var grants = roles.stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role.toString())).toList();
31+
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(token.getSubject(), null, grants);
32+
SecurityContextHolder.getContext().setAuthentication(auth);
33+
}
34+
filterChain.doFilter(request, response);
35+
}
36+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package br.com.itneki.nekicard.security;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.Email;
5+
import jakarta.validation.constraints.Pattern;
6+
import org.hibernate.validator.constraints.Length;
7+
8+
public record SignInDTO(
9+
@Schema(example = "user@neki-it.com.br",
10+
requiredMode = Schema.RequiredMode.REQUIRED)
11+
@Email(message = "O campo (email) deve conter um e-mail válido")
12+
@Pattern(regexp = "^[a-zA-Z0-9._%+-]+@(neki-it\\.com\\.br|neki\\.com\\.br)$",
13+
message = "Formato de e-mail inválido. O email deve ser do domínio neki-it.com.br ou neki.com.br")
14+
String email,
15+
16+
@Schema(example = "12345678",
17+
requiredMode = Schema.RequiredMode.REQUIRED)
18+
@Length(min = 8, max = 100, message = "A senha deve conter entre 8 e 100 caracteres")
19+
String password) {
20+
}
21+

0 commit comments

Comments
 (0)