-
Notifications
You must be signed in to change notification settings - Fork 71
SpringBoot2로 Rest api 만들기(8) – SpringSecurity를 이용한 인증 및 권한부여 #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
src/main/java/com/rest/api/advice/exception/CAuthenticationEntryPointException.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.rest.api.advice.exception; | ||
|
||
public class CAuthenticationEntryPointException extends RuntimeException { | ||
public CAuthenticationEntryPointException(String msg, Throwable t) { | ||
super(msg, t); | ||
} | ||
|
||
public CAuthenticationEntryPointException(String msg) { | ||
super(msg); | ||
} | ||
|
||
public CAuthenticationEntryPointException() { | ||
super(); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
src/main/java/com/rest/api/advice/exception/CEmailSigninFailedException.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.rest.api.advice.exception; | ||
|
||
public class CEmailSigninFailedException extends RuntimeException { | ||
public CEmailSigninFailedException(String msg, Throwable t) { | ||
super(msg, t); | ||
} | ||
|
||
public CEmailSigninFailedException(String msg) { | ||
super(msg); | ||
} | ||
|
||
public CEmailSigninFailedException() { | ||
super(); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 28 additions & 0 deletions
src/main/java/com/rest/api/config/security/CustomAccessDeniedHandler.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.rest.api.config.security; | ||
|
||
import lombok.extern.slf4j.Slf4j; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.security.access.AccessDeniedException; | ||
import org.springframework.security.web.access.AccessDeniedHandler; | ||
import org.springframework.stereotype.Component; | ||
|
||
import javax.servlet.RequestDispatcher; | ||
import javax.servlet.ServletException; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import java.io.IOException; | ||
|
||
@Slf4j | ||
@Component | ||
public class CustomAccessDeniedHandler implements AccessDeniedHandler { | ||
|
||
private static final Logger logger = LoggerFactory.getLogger(CustomAccessDeniedHandler.class); | ||
|
||
@Override | ||
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception) throws IOException, | ||
ServletException { | ||
RequestDispatcher dispatcher = request.getRequestDispatcher("/exception/accessdenied"); | ||
dispatcher.forward(request, response); | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
src/main/java/com/rest/api/config/security/CustomAuthenticationEntryPoint.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.rest.api.config.security; | ||
|
||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.security.core.AuthenticationException; | ||
import org.springframework.security.web.AuthenticationEntryPoint; | ||
import org.springframework.stereotype.Component; | ||
|
||
import javax.servlet.RequestDispatcher; | ||
import javax.servlet.ServletException; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import java.io.IOException; | ||
|
||
@Slf4j | ||
@Component | ||
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { | ||
|
||
@Override | ||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ex) throws IOException, | ||
ServletException { | ||
RequestDispatcher dispatcher = request.getRequestDispatcher("/exception/entrypoint"); | ||
dispatcher.forward(request, response); | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
src/main/java/com/rest/api/config/security/JwtAuthenticationFilter.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package com.rest.api.config.security; | ||
|
||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.web.filter.GenericFilterBean; | ||
|
||
import javax.servlet.FilterChain; | ||
import javax.servlet.ServletException; | ||
import javax.servlet.ServletRequest; | ||
import javax.servlet.ServletResponse; | ||
import javax.servlet.http.HttpServletRequest; | ||
import java.io.IOException; | ||
|
||
public class JwtAuthenticationFilter extends GenericFilterBean { | ||
|
||
private JwtTokenProvider jwtTokenProvider; | ||
|
||
// Jwt Provier 주입 | ||
public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) { | ||
this.jwtTokenProvider = jwtTokenProvider; | ||
} | ||
|
||
// Request로 들어오는 Jwt Token의 유효성을 검증(jwtTokenProvider.validateToken)하는 filter를 filterChain에 등록합니다. | ||
@Override | ||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { | ||
String token = jwtTokenProvider.resolveToken((HttpServletRequest) request); | ||
if (token != null && jwtTokenProvider.validateToken(token)) { | ||
Authentication auth = jwtTokenProvider.getAuthentication(token); | ||
SecurityContextHolder.getContext().setAuthentication(auth); | ||
} | ||
filterChain.doFilter(request, response); | ||
} | ||
} |
75 changes: 75 additions & 0 deletions
src/main/java/com/rest/api/config/security/JwtTokenProvider.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package com.rest.api.config.security; | ||
|
||
import io.jsonwebtoken.Claims; | ||
import io.jsonwebtoken.Jws; | ||
import io.jsonwebtoken.Jwts; | ||
import io.jsonwebtoken.SignatureAlgorithm; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
import org.springframework.security.core.userdetails.UserDetailsService; | ||
import org.springframework.stereotype.Component; | ||
|
||
import javax.annotation.PostConstruct; | ||
import javax.servlet.http.HttpServletRequest; | ||
import java.util.Base64; | ||
import java.util.Date; | ||
import java.util.List; | ||
|
||
@RequiredArgsConstructor | ||
@Component | ||
public class JwtTokenProvider { // JWT 토큰을 생성 및 검증 모듈 | ||
|
||
@Value("spring.jwt.secret") | ||
private String secretKey; | ||
|
||
private long tokenValidMilisecond = 1000L * 60 * 60; // 1시간만 토큰 유효 | ||
|
||
private final UserDetailsService userDetailsService; | ||
|
||
@PostConstruct | ||
protected void init() { | ||
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes()); | ||
} | ||
|
||
// Jwt 토큰 생성 | ||
public String createToken(String userPk, List<String> roles) { | ||
Claims claims = Jwts.claims().setSubject(userPk); | ||
claims.put("roles", roles); | ||
Date now = new Date(); | ||
return Jwts.builder() | ||
.setClaims(claims) // 데이터 | ||
.setIssuedAt(now) // 토큰 발행일자 | ||
.setExpiration(new Date(now.getTime() + tokenValidMilisecond)) // set Expire Time | ||
.signWith(SignatureAlgorithm.HS256, secretKey) // 암호화 알고리즘, secret값 세팅 | ||
.compact(); | ||
} | ||
|
||
// Jwt 토큰으로 인증 정보를 조회 | ||
public Authentication getAuthentication(String token) { | ||
UserDetails userDetails = userDetailsService.loadUserByUsername(this.getUserPk(token)); | ||
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); | ||
} | ||
|
||
// Jwt 토큰에서 회원 구별 정보 추출 | ||
public String getUserPk(String token) { | ||
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject(); | ||
} | ||
|
||
// Request의 Header에서 token 파싱 : "X-AUTH-TOKEN: jwt토큰" | ||
public String resolveToken(HttpServletRequest req) { | ||
return req.getHeader("X-AUTH-TOKEN"); | ||
} | ||
|
||
// Jwt 토큰의 유효성 + 만료일자 확인 | ||
public boolean validateToken(String jwtToken) { | ||
try { | ||
Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwtToken); | ||
return !claims.getBody().getExpiration().before(new Date()); | ||
} catch (Exception e) { | ||
return false; | ||
} | ||
} | ||
} |
53 changes: 53 additions & 0 deletions
src/main/java/com/rest/api/config/security/SecurityConfiguration.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package com.rest.api.config.security; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
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.web.builders.HttpSecurity; | ||
import org.springframework.security.config.annotation.web.builders.WebSecurity; | ||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; | ||
import org.springframework.security.config.http.SessionCreationPolicy; | ||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | ||
|
||
@RequiredArgsConstructor | ||
@Configuration | ||
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { | ||
|
||
private final JwtTokenProvider jwtTokenProvider; | ||
|
||
@Bean | ||
@Override | ||
public AuthenticationManager authenticationManagerBean() throws Exception { | ||
return super.authenticationManagerBean(); | ||
} | ||
|
||
@Override | ||
protected void configure(HttpSecurity http) throws Exception { | ||
http | ||
.httpBasic().disable() // rest api 이므로 기본설정 사용안함. 기본설정은 비인증시 로그인폼 화면으로 리다이렉트 된다. | ||
.csrf().disable() // rest api이므로 csrf 보안이 필요없으므로 disable처리. | ||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // jwt token으로 인증할것이므로 세션필요없으므로 생성안함. | ||
.and() | ||
.authorizeRequests() // 다음 리퀘스트에 대한 사용권한 체크 | ||
.antMatchers("/*/signin", "/*/signup").permitAll() // 가입 및 인증 주소는 누구나 접근가능 | ||
.antMatchers(HttpMethod.GET, "helloworld/**").permitAll() // hellowworld로 시작하는 GET요청 리소스는 누구나 접근가능 | ||
.antMatchers("/*/users").hasRole("ADMIN") | ||
.anyRequest().hasRole("USER") // 그외 나머지 요청은 모두 인증된 회원만 접근 가능 | ||
.and() | ||
.exceptionHandling().accessDeniedHandler(new CustomAccessDeniedHandler()) | ||
.and() | ||
.exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint()) | ||
.and() | ||
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class); // jwt token 필터를 id/password 인증 필터 전에 넣어라. | ||
|
||
} | ||
|
||
@Override // ignore swagger security config | ||
public void configure(WebSecurity web) { | ||
web.ignoring().antMatchers("/v2/api-docs", "/swagger-resources/**", | ||
"/swagger-ui.html", "/webjars/**", "/swagger/**"); | ||
|
||
} | ||
} |
25 changes: 25 additions & 0 deletions
src/main/java/com/rest/api/controller/exception/ExceptionController.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package com.rest.api.controller.exception; | ||
|
||
import com.rest.api.advice.exception.CAuthenticationEntryPointException; | ||
import com.rest.api.model.response.CommonResult; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.security.access.AccessDeniedException; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RequiredArgsConstructor | ||
@RestController | ||
@RequestMapping(value = "/exception") | ||
public class ExceptionController { | ||
|
||
@GetMapping(value = "/entrypoint") | ||
public CommonResult entrypointException() { | ||
throw new CAuthenticationEntryPointException(); | ||
} | ||
|
||
@GetMapping(value = "/accessdenied") | ||
public CommonResult accessdeniedException() { | ||
throw new AccessDeniedException(""); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.