Skip to content

Commit

Permalink
feat (Add AuthSecurity): token JWT Auth Spring
Browse files Browse the repository at this point in the history
Add token based authentication. For be able to POST, PUT and DELETE it necessary be autheticated. First get the JWT token then use the token for requests.
  • Loading branch information
SAMUEL BRISTOT LOLI committed May 15, 2022
1 parent 1869ba3 commit c17735e
Show file tree
Hide file tree
Showing 29 changed files with 528 additions and 16 deletions.
19 changes: 19 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
Expand All @@ -50,11 +55,20 @@
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
Expand All @@ -65,6 +79,11 @@
<artifactId>springfox-swagger-ui</artifactId>
<version>3.0.0</version>
</dependency>

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

<build>
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/samuelapp/demoshop/DemoShopApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.web.config.EnableSpringDataWebSupport;

@SpringBootApplication
@EnableSpringDataWebSupport
public class DemoShopApplication {
public static void main(String[] args) {
SpringApplication.run(DemoShopApplication.class, args);
Expand Down
35 changes: 35 additions & 0 deletions src/main/java/com/samuelapp/demoshop/config/ValidationHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.samuelapp.demoshop.config;

import com.samuelapp.demoshop.model.dto.FormErrorDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.ArrayList;
import java.util.List;

@RestControllerAdvice
public class ValidationHandler {

@Autowired
private MessageSource messageSource;

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public List<FormErrorDto> handle(MethodArgumentNotValidException exception) {
List<FormErrorDto> dto = new ArrayList<>();
List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();
fieldErrors.forEach(e -> {
String message = messageSource.getMessage(e, LocaleContextHolder.getLocale());
FormErrorDto error = new FormErrorDto(message, e.getField());
dto.add(error);
});
return dto;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.samuelapp.demoshop.config.security;

import com.samuelapp.demoshop.model.User;
import com.samuelapp.demoshop.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class AuthenticationService implements UserDetailsService {

@Autowired
UserRepository userRepository;

@Override
public User loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findByEmail(username).orElseThrow(()->new UsernameNotFoundException("User not found"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.samuelapp.demoshop.config.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@EnableWebSecurity
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

@Autowired
private AuthenticationService authenticationService;

@Autowired
private TokenService tokenService;

//Config about Authentication
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(authenticationService).passwordEncoder(new BCryptPasswordEncoder());
}

@Override
@Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}

//Config about Authorization
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers().disable().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.GET,"/").permitAll()
.antMatchers(HttpMethod.GET,"/swagger-ui/**").permitAll()
.antMatchers(HttpMethod.GET,"swagger-ui/index.html").permitAll()
.antMatchers(HttpMethod.POST,"/auth").permitAll()
.antMatchers(HttpMethod.GET,"/employees/**").permitAll()
.antMatchers(HttpMethod.GET, "/h2-console/**").permitAll()
.anyRequest().authenticated()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilterBefore(new TokenAuthFilter(tokenService), UsernamePasswordAuthenticationFilter.class);
}

//Config about static resources (js, css, images..)
@Override
public void configure(WebSecurity web) throws Exception {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.samuelapp.demoshop.config.security;

import com.samuelapp.demoshop.config.security.TokenService;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class TokenAuthFilter extends OncePerRequestFilter {

private TokenService tokenService;

public TokenAuthFilter(TokenService tokenService) {
this.tokenService = tokenService;
}

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = getTokenRequest(request);
if (tokenService.ValidateToken(token))
tokenService.authenticateToken(token);
filterChain.doFilter(request,response);
}

private String getTokenRequest(HttpServletRequest request) {
String token = request.getHeader("Authorization");
if (token==null || token.isEmpty() || !token.startsWith("Bearer ")){
return null;
}
return token.substring(7, token.length());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.samuelapp.demoshop.config.security;

import com.samuelapp.demoshop.model.User;
import com.samuelapp.demoshop.repository.UserRepository;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.Optional;

@Service
public class TokenService {

@Value("${demoshop.jwt.expiration}")
private String expiration;

@Value("${demoshop.jwt.secret}")
private String secret;

@Autowired
UserRepository userRepository;

public String buildToken(Authentication authenticate) {
User user = (User) authenticate.getPrincipal();
Date today = new Date();
Date expirationDate = new Date(today.getTime()+Long.parseLong(expiration));

return Jwts.builder()
.setIssuer("API demoShop")
.setSubject(String.valueOf(user.getId()))
.setIssuedAt(today)
.setExpiration(expirationDate)
.signWith(SignatureAlgorithm.HS256,secret)
.compact();
}

public Boolean ValidateToken(String token) {
try {
Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}

public void authenticateToken(String token) {
Claims body = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
Optional<User> optionalUser = userRepository.findById(Integer.valueOf(body.getSubject()));
User user = optionalUser.orElseThrow(()-> new UsernameNotFoundException("User not found"));
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user,null,user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
27 changes: 27 additions & 0 deletions src/main/java/com/samuelapp/demoshop/controler/AuthController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.samuelapp.demoshop.controler;

import com.samuelapp.demoshop.model.dto.LoginDto;
import com.samuelapp.demoshop.model.dto.TokenDto;
import com.samuelapp.demoshop.service.AuthService;
import org.springframework.beans.factory.annotation.Autowired;
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;

import javax.validation.Valid;

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

@Autowired
AuthService authService;

@PostMapping
public ResponseEntity<TokenDto> authenticate(@RequestBody @Valid LoginDto loginDto){
TokenDto token = authService.Authenticate(loginDto);
return ResponseEntity.ok(token);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@
import com.samuelapp.demoshop.repository.EmployeeRepository;
import com.samuelapp.demoshop.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.UriComponentsBuilder;
import springfox.documentation.annotations.ApiIgnore;

import javax.validation.Valid;
import java.net.URI;
import java.util.List;

Expand All @@ -20,15 +26,17 @@ public class EmployeeController {
private EmployeeService employeeService;

@PostMapping("/employees/")
public ResponseEntity<Employee> save(@RequestBody EmployeeDto employeeDto, UriComponentsBuilder uriBuilder){
public ResponseEntity<Employee> save(@RequestBody @Valid EmployeeDto employeeDto, UriComponentsBuilder uriBuilder){
Employee employee = employeeService.save(employeeDto);
URI uri = uriBuilder.path("/employees/{id}").buildAndExpand(employee.getId()).toUri();
return ResponseEntity.created(uri).body(employee);
}

@GetMapping("/employees/")
public ResponseEntity<List<Employee>> getAll(){
return ResponseEntity.ok(employeeService.getAll());
public ResponseEntity<Page<Employee>> getAll(@RequestParam(required = false) String name,
@PageableDefault(sort = "id", direction = Sort.Direction.ASC, page = 0, size = 10)
@ApiIgnore Pageable pageable){
return ResponseEntity.ok(employeeService.getAll(name, pageable));
}

@GetMapping("/employees/{id}")
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/com/samuelapp/demoshop/model/Role.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.samuelapp.demoshop.model;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity(name = "role")
@Getter
@Setter
public class Role implements GrantedAuthority {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;

private String name;

@ManyToMany(mappedBy = "roles")
private Set<User> users = new HashSet<User>();

@Override
public String getAuthority() {
return name;
}
}
Loading

0 comments on commit c17735e

Please sign in to comment.