Skip to content

Commit

Permalink
Merge pull request #81 from KMGeon/main
Browse files Browse the repository at this point in the history
Rest API - Security
  • Loading branch information
hannut91 authored Apr 18, 2023
2 parents 42bbe12 + 1a32250 commit a9d8bc6
Show file tree
Hide file tree
Showing 33 changed files with 1,002 additions and 491 deletions.
3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ dependencies {
// Use JUnit Jupiter API for testing.
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2'

//Security
implementation 'org.springframework.boot:spring-boot-starter-security'

// AssertJ
testImplementation 'org.assertj:assertj-core:3.18.1'

Expand Down
7 changes: 7 additions & 0 deletions app/src/main/java/com/codesoom/assignment/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@SpringBootApplication
public class App {
Expand All @@ -16,4 +18,9 @@ public static void main(String[] args) {
public Mapper dozerMapper() {
return DozerBeanMapperBuilder.buildDefault();
}

@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
Original file line number Diff line number Diff line change
@@ -1,36 +1,46 @@
package com.codesoom.assignment.application;

import com.codesoom.assignment.domain.Role;
import com.codesoom.assignment.domain.RoleRepository;
import com.codesoom.assignment.domain.User;
import com.codesoom.assignment.domain.UserRepository;
import com.codesoom.assignment.errors.LoginFailException;
import com.codesoom.assignment.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class AuthenticationService {
private final UserRepository userRepository;
private final JwtUtil jwtUtil;

public AuthenticationService(UserRepository userRepository,
JwtUtil jwtUtil) {
this.userRepository = userRepository;
this.jwtUtil = jwtUtil;
private final UserRepository userRepository;
private final JwtUtil jwtUtil;
private final PasswordEncoder passwordEncoder;
private final RoleRepository roleRepository;

public String login(String email, String password) {
User user = userRepository.findByEmail(email)
.orElseThrow(() -> new LoginFailException(email));

if (!user.authenticate(password, passwordEncoder)) {
throw new LoginFailException(email);
}

public String login(String email, String password) {
User user = userRepository.findByEmail(email)
.orElseThrow(() -> new LoginFailException(email));
return jwtUtil.encode(user.getId());
}

if (!user.authenticate(password)) {
throw new LoginFailException(email);
}
public Long parseToken(String accessToken) {
Claims claims = jwtUtil.decode(accessToken);
return claims.get("userId", Long.class);
}

return jwtUtil.encode(1L);
}
public List<Role> roles(Long userId) {
return roleRepository.findAllByUserId(userId);
}

public Long parseToken(String accessToken) {
Claims claims = jwtUtil.decode(accessToken);
return claims.get("userId", Long.class);
}
}
Original file line number Diff line number Diff line change
@@ -1,54 +1,69 @@
package com.codesoom.assignment.application;

import com.codesoom.assignment.domain.Role;
import com.codesoom.assignment.domain.RoleRepository;
import com.codesoom.assignment.domain.User;
import com.codesoom.assignment.domain.UserRepository;
import com.codesoom.assignment.dto.UserModificationData;
import com.codesoom.assignment.dto.UserRegistrationData;
import com.codesoom.assignment.errors.UserEmailDuplicationException;
import com.codesoom.assignment.errors.UserNotFoundException;
import com.github.dozermapper.core.Mapper;
import java.nio.file.AccessDeniedException;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;

@Service
@Transactional
@RequiredArgsConstructor
public class UserService {
private final Mapper mapper;
private final UserRepository userRepository;

public UserService(Mapper dozerMapper, UserRepository userRepository) {
this.mapper = dozerMapper;
this.userRepository = userRepository;
}
private final Mapper mapper;
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final RoleRepository roleRepository;

public User registerUser(UserRegistrationData registrationData) {
String email = registrationData.getEmail();
if (userRepository.existsByEmail(email)) {
throw new UserEmailDuplicationException(email);
}

User user = mapper.map(registrationData, User.class);
return userRepository.save(user);
public User registerUser(UserRegistrationData registrationData) {
String email = registrationData.getEmail();
if (userRepository.existsByEmail(email)) {
throw new UserEmailDuplicationException(email);
}
// User user = mapper.map(registrationData, User.class);
User user = userRepository.save(
mapper.map(registrationData, User.class));
Role role = roleRepository.save(new Role(user.getId(), "USER"));
user.changePassword(registrationData.getPassword(), passwordEncoder);

public User updateUser(Long id, UserModificationData modificationData) {
User user = findUser(id);
return user;
}

User source = mapper.map(modificationData, User.class);
user.changeWith(source);

return user;
public User updateUser(Long id, UserModificationData modificationData, Long userId)
throws AccessDeniedException {
if (id != userId) {
throw new AccessDeniedException("권한이 없습니다.");
}

public User deleteUser(Long id) {
User user = findUser(id);
user.destroy();
return user;
}
User user = findUser(id);

private User findUser(Long id) {
return userRepository.findByIdAndDeletedIsFalse(id)
.orElseThrow(() -> new UserNotFoundException(id));
}
User source = mapper.map(modificationData, User.class);
user.changeWith(source);

return user;
}

public User deleteUser(Long id) {
User user = findUser(id);
user.destroy();
return user;
}

private User findUser(Long id) {
return userRepository.findByIdAndDeletedIsFalse(id)
.orElseThrow(() -> new UserNotFoundException(id));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.codesoom.assignment.config;

import com.codesoom.assignment.application.AuthenticationService;
import com.codesoom.assignment.filter.AuthenticationErrorFilter;
import com.codesoom.assignment.filter.JwtAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;

import javax.servlet.Filter;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {

@Autowired
private AuthenticationService authenticationService;

@Override
protected void configure(HttpSecurity http) throws Exception {
Filter authenticationFilter = new JwtAuthenticationFilter(authenticationManager(),
authenticationService);
Filter authenticationErrorFilter = new AuthenticationErrorFilter();
http
.csrf().disable()
.headers()
.frameOptions().disable()
.and()
.addFilter(authenticationFilter)
.addFilterBefore(authenticationErrorFilter,
JwtAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(
new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
}

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ public ErrorResponse handleInvalidAccessTokenException() {
return new ErrorResponse("Invalid access token");
}

@ResponseStatus(HttpStatus.FORBIDDEN)
@ExceptionHandler(NotFoundRoleException.class)
public ErrorResponse notFoundRoleException() {
return new ErrorResponse("Not Found Role");
}

@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ConstraintViolationException.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import com.codesoom.assignment.domain.Product;
import com.codesoom.assignment.dto.ProductData;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
Expand Down Expand Up @@ -38,28 +40,29 @@ public Product detail(@PathVariable Long id) {
return productService.getProduct(id);
}


@PostMapping
@PreAuthorize("isAuthenticated() and hasAuthority('USER')")
@ResponseStatus(HttpStatus.CREATED)
public Product create(
@RequestAttribute Long userId,
@RequestBody @Valid ProductData productData
@RequestBody @Valid ProductData productData,
Authentication authentication
) {
return productService.createProduct(productData);
}

@PreAuthorize("isAuthenticated()")
@PatchMapping("{id}")
public Product update(
@RequestAttribute Long userId,
@PathVariable Long id,
@RequestBody @Valid ProductData productData
) {
return productService.updateProduct(id, productData);
}

@DeleteMapping("{id}")
@PreAuthorize("isAuthenticated()")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void destroy(
@RequestAttribute Long userId,
@PathVariable Long id
) {
productService.deleteProduct(id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
@RequestMapping("/session")
@CrossOrigin
public class SessionController {
private AuthenticationService authenticationService;
private final AuthenticationService authenticationService;

public SessionController(AuthenticationService authenticationService) {
this.authenticationService = authenticationService;
Expand All @@ -19,8 +19,7 @@ public SessionController(AuthenticationService authenticationService) {
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public SessionResponseData login(
@RequestBody SessionRequestData sessionRequestData
) {
@RequestBody SessionRequestData sessionRequestData) {
String email = sessionRequestData.getEmail();
String password = sessionRequestData.getPassword();

Expand Down
Loading

0 comments on commit a9d8bc6

Please sign in to comment.