Skip to content

Commit

Permalink
Merge pull request #85 from duohui12/duohui12
Browse files Browse the repository at this point in the history
7주차 과제 - 인가(Authorization) 구현하기
  • Loading branch information
hannut91 authored Aug 16, 2023
2 parents 42bbe12 + 57e02ea commit 017dfa1
Show file tree
Hide file tree
Showing 17 changed files with 277 additions and 15,820 deletions.
3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'

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

// Spring Developer Tools
developmentOnly 'org.springframework.boot:spring-boot-devtools'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
import com.codesoom.assignment.domain.User;
import com.codesoom.assignment.domain.UserRepository;
import com.codesoom.assignment.errors.LoginFailException;
import com.codesoom.assignment.errors.UserNotFoundException;
import com.codesoom.assignment.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class AuthenticationService {
private final UserRepository userRepository;
Expand All @@ -26,11 +29,16 @@ public String login(String email, String password) {
throw new LoginFailException(email);
}

return jwtUtil.encode(1L);
return jwtUtil.encode(user.getId());
}

public Long parseToken(String accessToken) {
Claims claims = jwtUtil.decode(accessToken);
return claims.get("userId", Long.class);
}

public List<String> getUserRoles(Long userId) {
User user = userRepository.findById(userId).orElseThrow(() -> new UserNotFoundException(userId));
return user.getRoles();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.codesoom.assignment.config;

import com.codesoom.assignment.application.AuthenticationService;
import com.codesoom.assignment.filters.AuthenticationErrorFilter;
import com.codesoom.assignment.filters.JwtAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
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.web.authentication.HttpStatusEntryPoint;

import javax.servlet.Filter;

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

@Autowired
private AuthenticationService authenticationService;

@Override
public void configure(HttpSecurity http) throws Exception {
Filter authenticationFilter = new JwtAuthenticationFilter(authenticationManager(), authenticationService);
Filter authenticationErrorFilter = new AuthenticationErrorFilter();

http
.csrf().disable()
.addFilter(authenticationFilter)
.addFilterBefore(authenticationErrorFilter
,JwtAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ public class WebJavaConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor);
//registry.addInterceptor(authenticationInterceptor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
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.web.bind.annotation.*;

import javax.validation.Valid;
Expand Down Expand Up @@ -40,16 +41,16 @@ public Product detail(@PathVariable Long id) {

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

@PatchMapping("{id}")
@PreAuthorize("isAuthenticated() and hasAuthority('USER')")
public Product update(
@RequestAttribute Long userId,
@PathVariable Long id,
@RequestBody @Valid ProductData productData
) {
Expand All @@ -58,8 +59,8 @@ public Product update(

@DeleteMapping("{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
@PreAuthorize("isAuthenticated() and hasAuthority('USER')")
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 @@ -6,6 +6,7 @@
import com.codesoom.assignment.dto.UserRegistrationData;
import com.codesoom.assignment.dto.UserResultData;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
Expand All @@ -28,6 +29,7 @@ UserResultData create(@RequestBody @Valid UserRegistrationData registrationData)
}

@PatchMapping("{id}")
@PreAuthorize("isAuthenticated() and hasAuthority('USER') and authentication.principal == #id" )
UserResultData update(
@PathVariable Long id,
@RequestBody @Valid UserModificationData modificationData
Expand All @@ -38,6 +40,7 @@ UserResultData update(

@DeleteMapping("{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
@PreAuthorize("isAuthenticated()")
void destroy(@PathVariable Long id) {
userService.deleteUser(id);
}
Expand Down
10 changes: 7 additions & 3 deletions app/src/main/java/com/codesoom/assignment/domain/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
@Getter
Expand All @@ -25,6 +25,10 @@ public class User {

private String password;

@ElementCollection(fetch = FetchType.EAGER)
@Builder.Default
private List<String> roles = new ArrayList<>();

@Builder.Default
private boolean deleted = false;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.codesoom.assignment.filters;

import com.codesoom.assignment.errors.InvalidTokenException;
import org.springframework.http.HttpStatus;

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

public class AuthenticationErrorFilter extends HttpFilter{
@Override
public void doFilter(
HttpServletRequest request
, HttpServletResponse response
, FilterChain chain) throws IOException, ServletException {

try {
chain.doFilter(request, response);
}catch(InvalidTokenException exception){
response.sendError(HttpStatus.UNAUTHORIZED.value());
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.codesoom.assignment.filters;

import com.codesoom.assignment.application.AuthenticationService;
import com.codesoom.assignment.security.UserAuthentication;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

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;

public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
private final AuthenticationService authenticationService;

public JwtAuthenticationFilter(AuthenticationManager authenticationManager, AuthenticationService authenticationService) {
super(authenticationManager);
this.authenticationService = authenticationService;
}

@Override
protected void doFilterInternal(HttpServletRequest request
, HttpServletResponse response
, FilterChain chain) throws IOException, ServletException {

String authorization = request.getHeader("Authorization");

if (authorization != null) {
String accessToken = authorization.substring("Bearer ".length());
Long userId = authenticationService.parseToken(accessToken);
List<String> roles = authenticationService.getUserRoles(userId);
Authentication authentication = new UserAuthentication(userId,roles);

SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(authentication);
}

chain.doFilter(request,response);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.codesoom.assignment.security;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

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

public class UserAuthentication extends AbstractAuthenticationToken {

private final Long userId;

public UserAuthentication(Long userId, List<String> roles) {
super(authorities(roles));
this.userId = userId;
}

private static List<GrantedAuthority> authorities(List<String> roles) {
//return roles.stream().map(SimpleGrantedAuthority::new)
// .collect(Collectors.toList());

List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("USER"));
return authorities;
}

@Override
public Object getCredentials() {
return null;
}

@Override
public Object getPrincipal() {
return userId;
}

@Override
public boolean isAuthenticated() {
return true;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public JwtUtil(@Value("${jwt.secret}") String secret) {

public String encode(Long userId) {
return Jwts.builder()
.claim("userId", 1L)
.claim("userId", userId)
.signWith(key)
.compact();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ void setUp() {
userRepository, jwtUtil);

User user = User.builder()
.id(1L)
.password("test")
.build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

package com.codesoom.assignment.application;

import com.codesoom.assignment.errors.ProductNotFoundException;
import com.codesoom.assignment.domain.Product;
import com.codesoom.assignment.domain.ProductRepository;
import com.codesoom.assignment.dto.ProductData;
import com.codesoom.assignment.errors.ProductNotFoundException;
import com.github.dozermapper.core.DozerBeanMapperBuilder;
import com.github.dozermapper.core.Mapper;
import org.junit.jupiter.api.BeforeEach;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ void updateUserWithNotExistedId() {
verify(userRepository).findByIdAndDeletedIsFalse(100L);
}


@Test
void updateUserWithDeletedId() {
UserModificationData modificationData = UserModificationData.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import java.util.Collections;
import java.util.List;

import static org.hamcrest.Matchers.containsString;
Expand Down Expand Up @@ -79,6 +80,8 @@ void setUp() {

given(authenticationService.parseToken(VALID_TOKEN)).willReturn(1L);

given(authenticationService.getUserRoles(1L)).willReturn(Collections.singletonList("USER"));

given(authenticationService.parseToken(INVALID_TOKEN))
.willThrow(new InvalidTokenException(INVALID_TOKEN));
}
Expand Down Expand Up @@ -256,7 +259,7 @@ void destroyWithNotExistedProduct() throws Exception {
@Test
void destroyWithInvalidAccessToken() throws Exception {
mockMvc.perform(
patch("/products/1")
delete("/products/1")
.accept(MediaType.APPLICATION_JSON_UTF8)
.contentType(MediaType.APPLICATION_JSON)
.content("{\"name\":\"쥐순이\",\"maker\":\"냥이월드\"," +
Expand Down
Loading

0 comments on commit 017dfa1

Please sign in to comment.