Skip to content

Commit

Permalink
Merge pull request #40 from PascalWo/#36-authetication-full
Browse files Browse the repository at this point in the history
#36 authetication full
  • Loading branch information
PascalWo authored May 23, 2022
2 parents 0cd64d8 + 3dac72e commit ce3e2ec
Show file tree
Hide file tree
Showing 30 changed files with 579 additions and 42 deletions.
19 changes: 19 additions & 0 deletions backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,25 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package de.neuefische.backend.security.config;

import de.neuefische.backend.security.filter.JWTAuthFilter;
import de.neuefische.backend.security.service.AppUserDetailsService;
import org.springframework.context.annotation.Bean;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

private final AppUserDetailsService appUserDetailsService;
private final JWTAuthFilter jwtAuthFilter;

public SecurityConfig(AppUserDetailsService appUserDetailsService, JWTAuthFilter jwtAuthFilter) {
this.appUserDetailsService = appUserDetailsService;
this.jwtAuthFilter = jwtAuthFilter;
}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(appUserDetailsService);
}

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

@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/auth/login").permitAll()
.antMatchers("/**").permitAll()
.and().addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package de.neuefische.backend.security.controller;

import de.neuefische.backend.security.dto.AppUserDto;
import de.neuefische.backend.security.service.JWTUtilService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
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("/auth")
public class AppUserAuthController {

private final AuthenticationManager authenticationManager;
private final JWTUtilService jwtUtilService;

@Autowired
public AppUserAuthController(AuthenticationManager authenticationManager, JWTUtilService jwtUtilService) {
this.authenticationManager = authenticationManager;
this.jwtUtilService = jwtUtilService;
}

@PostMapping("/login")
public String login(@RequestBody AppUserDto appUser) {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(appUser.getUsername(), appUser.getPassword()));
return jwtUtilService.createToken(appUser.getUsername());

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package de.neuefische.backend.security.dto;

import lombok.Data;
import lombok.Getter;

@Data
@Getter
public class AppUserDto {

private String username;
private String password;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package de.neuefische.backend.security.filter;

import de.neuefische.backend.security.service.JWTUtilService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
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;
import java.util.List;

@Slf4j
@Component
public class JWTAuthFilter extends OncePerRequestFilter {

private final JWTUtilService jwtUtilService;

@Autowired
public JWTAuthFilter(JWTUtilService jwtUtilService) {
this.jwtUtilService = jwtUtilService;
}

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = getAuthToken(request);

try{
if (token != null && !token.isBlank()){
String username = jwtUtilService.extractUsername(token);
setContext(username);
} else {
log.error("Token is null or blank.");
}
} catch (Exception ex){
log.error("JWT error." + ex.getMessage());
}

filterChain.doFilter(request, response);
}

private void setContext(String username) {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(username, "", List.of());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}

private String getAuthToken(HttpServletRequest request) {
String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);

if (authHeader != null) {
return authHeader.replace("Bearer ", "").trim();
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package de.neuefische.backend.security.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Document("appusers")

public class AppUser {
@Id
private String id;

private String username;
private String password;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package de.neuefische.backend.security.repository;

import de.neuefische.backend.security.model.AppUser;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface AppUserRepository extends MongoRepository<AppUser, String> {
Optional<AppUser> findByUsername(String username);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package de.neuefische.backend.security.service;

import de.neuefische.backend.security.model.AppUser;
import de.neuefische.backend.security.repository.AppUserRepository;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class AppUserDetailsService implements UserDetailsService {

private final AppUserRepository appUserRepository;

public AppUserDetailsService(AppUserRepository appUserRepository) {
this.appUserRepository = appUserRepository;
}

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
AppUser appUser = appUserRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("Username not found"));
return new User(appUser.getUsername(), appUser.getPassword(), List.of());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package de.neuefische.backend.security.service;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.HashMap;

@Service
public class JWTUtilService {

@Value("${neuefische.lapamealfactory.jwt.secret}")
private String jwtSecret;

public String createToken(String username) {

return Jwts.builder()
.setClaims(new HashMap<>())
.setSubject(username)
.setIssuedAt(Date.from(Instant.now()))
.setExpiration(Date.from(Instant.now().plus(Duration.ofHours(4))))
.signWith(SignatureAlgorithm.HS256, jwtSecret)
.compact();
}

public String extractUsername(String token) {
return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject();
}
}
1 change: 1 addition & 0 deletions backend/src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
spring.data.mongodb.uri=${MONGO_DB_URI}
spoonacular.lapamealfactory.api.key=${SPOONACULAR_API_KEY}
neuefische.lapamealfactory.jwt.secret=${JWT_SECRET}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import static org.junit.jupiter.api.Assertions.assertTrue;

@SpringBootTest
class BackendApplicationTests {

@Test
void contextLoads() {
assertTrue(true);
}

}
Loading

0 comments on commit ce3e2ec

Please sign in to comment.