-
Notifications
You must be signed in to change notification settings - Fork 0
#1 인증 토큰 spring security, JWT #5
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,49 @@ | ||
package com.pay.billing; | ||
|
||
import com.pay.billing.domain.model.Role; | ||
import com.pay.billing.domain.model.User; | ||
import com.pay.billing.service.UserService; | ||
import org.modelmapper.ModelMapper; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.CommandLineRunner; | ||
import org.springframework.boot.SpringApplication; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
import org.springframework.context.annotation.Bean; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
|
||
@SpringBootApplication | ||
public class BillingApplication { | ||
public class BillingApplication implements CommandLineRunner { | ||
|
||
@Autowired | ||
UserService userService; | ||
|
||
public static void main(String[] args) { | ||
SpringApplication.run(BillingApplication.class, args); | ||
} | ||
|
||
@Bean | ||
public ModelMapper modelMapper() { | ||
return new ModelMapper(); | ||
} | ||
|
||
@Override | ||
public void run(String... params) throws Exception { | ||
User admin = new User(); | ||
admin.setUsername("admin"); | ||
admin.setPassword("admin"); | ||
admin.setEmail("admin@email.com"); | ||
admin.setRoles(new ArrayList<Role>(Arrays.asList(Role.ROLE_ADMIN))); | ||
|
||
userService.signup(admin); | ||
|
||
User client = new User(); | ||
client.setUsername("client"); | ||
client.setPassword("client"); | ||
client.setEmail("client@email.com"); | ||
client.setRoles(new ArrayList<Role>(Arrays.asList(Role.ROLE_CLIENT))); | ||
|
||
userService.signup(client); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package com.pay.billing.common.config; | ||
|
||
import com.google.common.base.Predicates; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import springfox.documentation.builders.ApiInfoBuilder; | ||
import springfox.documentation.builders.PathSelectors; | ||
import springfox.documentation.builders.RequestHandlerSelectors; | ||
import springfox.documentation.service.ApiInfo; | ||
import springfox.documentation.service.ApiKey; | ||
import springfox.documentation.service.AuthorizationScope; | ||
import springfox.documentation.service.SecurityReference; | ||
import springfox.documentation.service.Tag; | ||
import springfox.documentation.spi.DocumentationType; | ||
import springfox.documentation.spi.service.contexts.SecurityContext; | ||
import springfox.documentation.spring.web.plugins.Docket; | ||
import springfox.documentation.swagger2.annotations.EnableSwagger2; | ||
|
||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
@Configuration | ||
@EnableSwagger2 | ||
public class SwaggerConfig { | ||
@Bean | ||
public Docket swaggerApi() { | ||
return new Docket(DocumentationType.SWAGGER_2)// | ||
.select()// | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기 주석은 복붙해온 소스의 주석을 지우다가 남아있는걸까요? |
||
.apis(RequestHandlerSelectors.any())// | ||
.paths(Predicates.not(PathSelectors.regex("/error")))// | ||
.build()// | ||
.apiInfo(swaggerInfo()) | ||
.useDefaultResponseMessages(false)// | ||
.securitySchemes(Collections.singletonList(apiKey())) | ||
.securityContexts(Collections.singletonList(securityContext())) | ||
.tags(new Tag("users", "Operations about users"))// | ||
.genericModelSubstitutes(Optional.class); | ||
} | ||
|
||
private ApiInfo swaggerInfo() { | ||
return new ApiInfoBuilder().title("Spring API Documentation") | ||
.description("앱 개발시 사용되는 서버 API에 대한 연동 문서입니다").build(); | ||
} | ||
|
||
private ApiKey apiKey() { | ||
return new ApiKey("Authorization", "Authorization", "header"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이런것들은 어떤 의도로 추가하신건지 주석이 필요하네요 |
||
} | ||
|
||
private SecurityContext securityContext() { | ||
return SecurityContext.builder() | ||
.securityReferences(defaultAuth()) | ||
.forPaths(PathSelectors.any()) | ||
.build(); | ||
} | ||
|
||
private List<SecurityReference> defaultAuth() { | ||
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); | ||
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; | ||
authorizationScopes[0] = authorizationScope; | ||
return Arrays.asList(new SecurityReference("Authorization", authorizationScopes)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package com.pay.billing.common.config; | ||
|
||
import com.pay.billing.common.security.JwtTokenFilterConfigurer; | ||
import com.pay.billing.common.security.JwtTokenProvider; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.security.authentication.AuthenticationManager; | ||
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.builders.WebSecurity; | ||
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.bcrypt.BCryptPasswordEncoder; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
|
||
@Configuration | ||
@EnableWebSecurity | ||
@EnableGlobalMethodSecurity(prePostEnabled = true) | ||
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { | ||
|
||
@Autowired | ||
private JwtTokenProvider jwtTokenProvider; | ||
|
||
@Override | ||
protected void configure(HttpSecurity http) throws Exception { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 메소드에 있는 전체 설정 한줄한줄에 대한 의도를 설명이 가능하실까요? |
||
|
||
// Disable CSRF (cross site request forgery) | ||
http.csrf().disable(); | ||
|
||
// No session will be created or used by spring security | ||
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); | ||
|
||
// Entry points | ||
http.authorizeRequests()// | ||
.antMatchers("/users/signin").permitAll()// | ||
.antMatchers("/users/signup").permitAll()// | ||
.antMatchers("/h2-console/**/**").permitAll() | ||
// Disallow everything else.. | ||
.anyRequest().authenticated(); | ||
|
||
// If a user try to access a resource without having enough permissions | ||
http.exceptionHandling().accessDeniedPage("/login"); | ||
|
||
// Apply JWT | ||
http.apply(new JwtTokenFilterConfigurer(jwtTokenProvider)); | ||
|
||
// Optional, if you want to test the API from a browser | ||
// http.httpBasic(); | ||
} | ||
|
||
@Override | ||
public void configure(WebSecurity web) throws Exception { | ||
// Allow swagger to be accessed without authentication | ||
web.ignoring().antMatchers("/v2/api-docs")// | ||
.antMatchers("/swagger-resources/**")// | ||
.antMatchers("/swagger-ui.html")// | ||
.antMatchers("/configuration/**")// | ||
.antMatchers("/webjars/**")// | ||
.antMatchers("/public") | ||
|
||
// Un-secure H2 Database (for testing purposes, H2 console shouldn't be unprotected in production) | ||
.and() | ||
.ignoring() | ||
.antMatchers("/h2-console/**/**");; | ||
} | ||
|
||
@Bean | ||
public PasswordEncoder passwordEncoder() { | ||
return new BCryptPasswordEncoder(12); | ||
} | ||
|
||
@Override | ||
@Bean | ||
public AuthenticationManager authenticationManagerBean() throws Exception { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기 들여쓰기가 다른 클래스들의 들여쓰기와 다른 것을 보니 이것 또한 베껴온 소스의 느낌이 나네요. 스스로 작성한 코드가 아니면 리뷰를 받아도 그 리뷰내용을 암기만 할뿐 이해가 되지 않기 때문에 리뷰의 의미가 없어집니다 |
||
return super.authenticationManagerBean(); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package com.pay.billing.common.exception; | ||
|
||
import org.springframework.http.HttpStatus; | ||
|
||
public class CustomException extends RuntimeException { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
private final String message; | ||
private final HttpStatus httpStatus; | ||
|
||
public CustomException(String message, HttpStatus httpStatus) { | ||
this.message = message; | ||
this.httpStatus = httpStatus; | ||
} | ||
|
||
@Override | ||
public String getMessage() { | ||
return message; | ||
} | ||
|
||
public HttpStatus getHttpStatus() { | ||
return httpStatus; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package com.pay.billing.common.security; | ||
|
||
import com.pay.billing.common.exception.CustomException; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
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; | ||
|
||
// We should use OncePerRequestFilter since we are doing a database call, there is no point in doing this more than once | ||
public class JwtTokenFilter extends OncePerRequestFilter { | ||
|
||
private JwtTokenProvider jwtTokenProvider; | ||
|
||
public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) { | ||
this.jwtTokenProvider = jwtTokenProvider; | ||
} | ||
|
||
@Override | ||
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { | ||
String token = jwtTokenProvider.resolveToken(httpServletRequest); | ||
try { | ||
if (token != null && jwtTokenProvider.validateToken(token)) { | ||
Authentication auth = jwtTokenProvider.getAuthentication(token); | ||
SecurityContextHolder.getContext().setAuthentication(auth); | ||
} | ||
} catch (CustomException ex) { | ||
//this is very important, since it guarantees the user is not authenticated at all | ||
SecurityContextHolder.clearContext(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기서 |
||
httpServletResponse.sendError(ex.getHttpStatus().value(), ex.getMessage()); | ||
return; | ||
} | ||
|
||
filterChain.doFilter(httpServletRequest, httpServletResponse); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.pay.billing.common.security; | ||
|
||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter; | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
import org.springframework.security.web.DefaultSecurityFilterChain; | ||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | ||
|
||
public class JwtTokenFilterConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> { | ||
|
||
private JwtTokenProvider jwtTokenProvider; | ||
|
||
public JwtTokenFilterConfigurer(JwtTokenProvider jwtTokenProvider) { | ||
this.jwtTokenProvider = jwtTokenProvider; | ||
} | ||
|
||
@Override | ||
public void configure(HttpSecurity http) throws Exception { | ||
JwtTokenFilter customFilter = new JwtTokenFilter(jwtTokenProvider); | ||
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class); | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이거는 왜 넣어주신건가요?