diff --git a/spring-security-jwt/src/main/java/com/security/jwt/config/SecurityConfig.java b/spring-security-jwt/src/main/java/com/security/jwt/config/SecurityConfig.java index 67e21d9d..02d4813b 100644 --- a/spring-security-jwt/src/main/java/com/security/jwt/config/SecurityConfig.java +++ b/spring-security-jwt/src/main/java/com/security/jwt/config/SecurityConfig.java @@ -1,6 +1,8 @@ package com.security.jwt.config; -import com.security.jwt.jwt.JwtAuthenticationFilter; +import com.security.jwt.config.jwt.JwtAuthenticationFilter; +import com.security.jwt.config.jwt.JwtAuthorizationFilter; +import com.security.jwt.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -17,6 +19,7 @@ import org.springframework.web.filter.CorsFilter; public class SecurityConfig extends WebSecurityConfigurerAdapter { private final CorsFilter corsFilter; + private final UserRepository userRepository; @Bean public BCryptPasswordEncoder passwordEncoder() { @@ -33,6 +36,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { .formLogin().disable() .httpBasic().disable() .addFilter(new JwtAuthenticationFilter(authenticationManager())) // AuthenticationManager + .addFilter(new JwtAuthorizationFilter(authenticationManager(), userRepository)) .authorizeRequests() .antMatchers("/api/v1/user/**") .access("hasRole('ROLE_USER') or hasRole('ROLE_MANAGER') or hasRole('ROLE_ADMIN')") diff --git a/spring-security-jwt/src/main/java/com/security/jwt/auth/PrincipalDetails.java b/spring-security-jwt/src/main/java/com/security/jwt/config/auth/PrincipalDetails.java similarity index 96% rename from spring-security-jwt/src/main/java/com/security/jwt/auth/PrincipalDetails.java rename to spring-security-jwt/src/main/java/com/security/jwt/config/auth/PrincipalDetails.java index f289f677..226eb42c 100644 --- a/spring-security-jwt/src/main/java/com/security/jwt/auth/PrincipalDetails.java +++ b/spring-security-jwt/src/main/java/com/security/jwt/config/auth/PrincipalDetails.java @@ -1,4 +1,4 @@ -package com.security.jwt.auth; +package com.security.jwt.config.auth; import com.security.jwt.model.User; import lombok.Data; diff --git a/spring-security-jwt/src/main/java/com/security/jwt/auth/PrincipalDetailsService.java b/spring-security-jwt/src/main/java/com/security/jwt/config/auth/PrincipalDetailsService.java similarity index 96% rename from spring-security-jwt/src/main/java/com/security/jwt/auth/PrincipalDetailsService.java rename to spring-security-jwt/src/main/java/com/security/jwt/config/auth/PrincipalDetailsService.java index 39291b38..9f7726d8 100644 --- a/spring-security-jwt/src/main/java/com/security/jwt/auth/PrincipalDetailsService.java +++ b/spring-security-jwt/src/main/java/com/security/jwt/config/auth/PrincipalDetailsService.java @@ -1,4 +1,4 @@ -package com.security.jwt.auth; +package com.security.jwt.config.auth; import com.security.jwt.model.User; import com.security.jwt.repository.UserRepository; diff --git a/spring-security-jwt/src/main/java/com/security/jwt/jwt/JwtAuthenticationFilter.java b/spring-security-jwt/src/main/java/com/security/jwt/config/jwt/JwtAuthenticationFilter.java similarity index 93% rename from spring-security-jwt/src/main/java/com/security/jwt/jwt/JwtAuthenticationFilter.java rename to spring-security-jwt/src/main/java/com/security/jwt/config/jwt/JwtAuthenticationFilter.java index 9cee8b31..6f96ea6b 100644 --- a/spring-security-jwt/src/main/java/com/security/jwt/jwt/JwtAuthenticationFilter.java +++ b/spring-security-jwt/src/main/java/com/security/jwt/config/jwt/JwtAuthenticationFilter.java @@ -1,9 +1,9 @@ -package com.security.jwt.jwt; +package com.security.jwt.config.jwt; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.fasterxml.jackson.databind.ObjectMapper; -import com.security.jwt.auth.PrincipalDetails; +import com.security.jwt.config.auth.PrincipalDetails; import com.security.jwt.model.User; import lombok.RequiredArgsConstructor; import org.springframework.security.authentication.AuthenticationManager; @@ -84,11 +84,11 @@ public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilte // RSA방식이 아닌 HASH 방식 (secret key 필요 ) String jwtToken = JWT.create() .withSubject("wj토큰") - .withExpiresAt(new Date(System.currentTimeMillis()+(60000 * 10))) + .withExpiresAt(new Date(System.currentTimeMillis()+JwtProperties.EXPIRATION_TIME)) .withClaim("id", principalDetails.getUser().getId()) .withClaim("username", principalDetails.getUser().getUsername()) - .sign(Algorithm.HMAC512("wj")); + .sign(Algorithm.HMAC512(JwtProperties.SECRET)); - response.addHeader("Authorization", "Bearer " + jwtToken); + response.addHeader(JwtProperties.HEADER_STRING, JwtProperties.TOKEN_PREFIX + jwtToken); } } diff --git a/spring-security-jwt/src/main/java/com/security/jwt/config/jwt/JwtAuthorizationFilter.java b/spring-security-jwt/src/main/java/com/security/jwt/config/jwt/JwtAuthorizationFilter.java new file mode 100644 index 00000000..ea998309 --- /dev/null +++ b/spring-security-jwt/src/main/java/com/security/jwt/config/jwt/JwtAuthorizationFilter.java @@ -0,0 +1,65 @@ +package com.security.jwt.config.jwt; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.security.jwt.config.auth.PrincipalDetails; +import com.security.jwt.model.User; +import com.security.jwt.repository.UserRepository; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +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; + +// 시큐리티가 가지고 있는 BasicAuthenticationFilter 는 +// 권한이나 인증이 필요한 특정 주소를 요청 했을 때 필터를 거치게 되고 +// 만약 권한, 인증이 필요없는 주소면 필터를 거치지 않는다. +public class JwtAuthorizationFilter extends BasicAuthenticationFilter { + + private UserRepository userRepository; + + public JwtAuthorizationFilter(AuthenticationManager authenticationManager, UserRepository userRepository) { + super(authenticationManager); + this.userRepository = userRepository; + } + + // 인증이나 권한이 필요한 주소 요청이 있을 때 해당 필터를 거친다. + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { +// super.doFilterInternal(request, response, chain); + System.out.println("인증이나 권한이 필요한 주소 요청 : JwtAuthorizationFilter "); + + String jwtHeader = request.getHeader(JwtProperties.HEADER_STRING); + System.out.println("jwtHeader " + jwtHeader); + + // jwtHeader가 있는지 확인 + if(jwtHeader == null || !jwtHeader.startsWith(JwtProperties.TOKEN_PREFIX)) { + chain.doFilter(request, response); + return; + } + + // jwt token 검증 + String jwtToken = request.getHeader(JwtProperties.HEADER_STRING).replace(JwtProperties.TOKEN_PREFIX,""); + + String username = + JWT.require(Algorithm.HMAC512(JwtProperties.SECRET)).build().verify(jwtToken).getClaim("username").asString(); + // 서명이 정상적으로 완료 + if(username != null) { + User userEntity = userRepository.findByUsername(username); + PrincipalDetails principalDetails = new PrincipalDetails(userEntity); + // jwt token 서명이 정상이면 Authentication 객체를 만들어 준다. + Authentication authentication = + new UsernamePasswordAuthenticationToken(principalDetails, null, principalDetails.getAuthorities()); + + // 강제로 시큐리티의 세션에 접근하여 Authentication 객체를 저장 + SecurityContextHolder.getContext().setAuthentication(authentication); + } + chain.doFilter(request, response); + } +} diff --git a/spring-security-jwt/src/main/java/com/security/jwt/config/jwt/JwtProperties.java b/spring-security-jwt/src/main/java/com/security/jwt/config/jwt/JwtProperties.java new file mode 100644 index 00000000..d42aac82 --- /dev/null +++ b/spring-security-jwt/src/main/java/com/security/jwt/config/jwt/JwtProperties.java @@ -0,0 +1,8 @@ +package com.security.jwt.config.jwt; + +public interface JwtProperties { + String SECRET = "wj"; + int EXPIRATION_TIME = 60000*10; + String TOKEN_PREFIX = "Bearer "; + String HEADER_STRING = "Authorization"; +} diff --git a/spring-security-jwt/src/main/java/com/security/jwt/controller/RestApiController.java b/spring-security-jwt/src/main/java/com/security/jwt/controller/RestApiController.java index 82b8a537..f8462d5f 100644 --- a/spring-security-jwt/src/main/java/com/security/jwt/controller/RestApiController.java +++ b/spring-security-jwt/src/main/java/com/security/jwt/controller/RestApiController.java @@ -33,4 +33,22 @@ public class RestApiController { userRepository.save(user); return "회원가입완료"; } + + // user, manager, admin 접근 가능 + @GetMapping("/api/v1/user") + public String user() { + return "user"; + } + + // manager, admin 접근가능 + @GetMapping("/api/v1/manager") + public String manager() { + return "manager"; + } + + // admin 접근가능 + @GetMapping("/api/v1/admin") + public String admin() { + return "admin"; + } }