Skip to content

Commit

Permalink
Merge pull request #200 from INFP-Study/feature/login_config
Browse files Browse the repository at this point in the history
JWT추가
  • Loading branch information
choisungwook authored Dec 11, 2021
2 parents a76292b + 3191fa6 commit 8abdd9c
Show file tree
Hide file tree
Showing 15 changed files with 413 additions and 121 deletions.
7 changes: 2 additions & 5 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ repositories {

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security' // 필요할 때 주석 해제하고 사용
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.6'
implementation group: 'com.github.ulisesbocchio', name: 'jasypt-spring-boot-starter', version: '3.0.3'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
Expand All @@ -34,13 +35,9 @@ dependencies {
implementation group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.9.2'
implementation group: 'io.springfox', name: 'springfox-swagger2', version: '2.9.2'
implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-aws', version: '2.2.6.RELEASE'

// implementation 'org.springframework.session:spring-session-jdbc'

runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'

implementation 'com.sun.xml.bind:jaxb1-impl:2.2.5.1'
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
import java.util.Collection;
import java.util.Map;

// Authentication 객체에 저장할 수 있는 유일한 타입
/***
* 인증정보 관리
*/
public class PrincipalDetails implements UserDetails, OAuth2User, Serializable {

private Account account;
private Map<String, Object> attributes;

Expand Down Expand Up @@ -47,9 +48,12 @@ public String getName() {
return (String) attributes.get("name");
}

// account의 권한을 리턴한다.
/***
* role 리턴
* @return
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
public Collection<GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collect = new ArrayList<>();
collect.add(new GrantedAuthority() {
@Override
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
package com.infp.ciat.config.security;

import com.infp.ciat.config.auth.OAuth2DetailsService;
import com.infp.ciat.config.security.filter.JWTCheckFilter;
import com.infp.ciat.config.security.filter.JWTFilter;
import com.infp.ciat.config.security.jwt.JWTUtils;
import com.infp.ciat.user.service.AccountService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
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.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/***
* 스프링시큐리티 설정
*/
Expand All @@ -36,10 +32,11 @@
@EnableWebSecurity
@Slf4j
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private AccountService accountService;
@Autowired
private OAuth2DetailsService oAuth2DetailesService;

private JWTUtils jwtUtils = new JWTUtils();
/***
* default 패스워드 암호화알고리즘 사용 설정
* @return
Expand All @@ -56,39 +53,43 @@ public PasswordEncoder passwordEncoder(){
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 필터등록
http.addFilter(new JWTFilter(authenticationManagerBean(), jwtUtils))
.addFilterBefore(new JWTCheckFilter(authenticationManagerBean(),
accountService, jwtUtils), UsernamePasswordAuthenticationFilter.class);

http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

http.formLogin().disable()
.httpBasic().disable();

http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/v1/user/signin").permitAll()
.antMatchers("/api/v1/user/signup").permitAll()
.antMatchers("/api/v1/session/**").permitAll()
// .antMatchers("/api/v1/session/**").permitAll()
.antMatchers(HttpMethod.GET, "/api/v1/menu").permitAll()
.antMatchers(HttpMethod.GET, "/api/v1/menu/**").permitAll()
.antMatchers("/healthcheck").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl ("/api/v1/user/login")
.usernameParameter("email")
.passwordParameter("password")
.failureHandler(new LoginFailHandler())
.successHandler(new LoginSuccessHandler())
.and()
.logout()
.logoutUrl("/api/v1/user/logout")
.permitAll()
.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK))
.and()
.sessionManagement()
.invalidSessionUrl("/api/v1/session/invalid")
.and()
.cors()
.configurationSource(corsConfigurationSource())
.and()
.oauth2Login()
.userInfoEndpoint()
.userService(oAuth2DetailesService);
.anyRequest().authenticated();

http.cors()
.configurationSource(corsConfigurationSource());

http.oauth2Login().userInfoEndpoint().userService(oAuth2DetailesService);
}

/***
* 기본 authenticationManger를 Bean으로 등록한다.
* @return
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

@Bean
public CorsConfigurationSource corsConfigurationSource() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.infp.ciat.config.security.filter;

import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.google.gson.JsonObject;
import com.infp.ciat.config.auth.PrincipalDetails;
import com.infp.ciat.config.security.jwt.JWTUtils;
import com.infp.ciat.config.security.jwt.JWTVerifyResult;
import com.infp.ciat.user.entity.Account;
import com.infp.ciat.user.service.AccountService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
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;
import java.io.PrintWriter;
import java.util.Optional;

/***
* JWT인증 검사
*/
@Slf4j
public class JWTCheckFilter extends OncePerRequestFilter {
private final AccountService accountService;
private final AuthenticationManager authenticationManager;
private final JWTUtils jwtUtils;

public JWTCheckFilter(
AuthenticationManager authenticationManager,
AccountService accountService,
JWTUtils jwtUtils)
{
this.accountService = accountService;
this.authenticationManager = authenticationManager;
this.jwtUtils = jwtUtils;
}

@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException
{
String authorization = request.getHeader("Authorization");
if(authorization == null || !authorization.startsWith("Bearer")){
filterChain.doFilter(request, response);
return;
}

JWTVerifyResult verify_result = null;
try{
verify_result = this.jwtUtils.verify(authorization.substring("Bearer ".length()));
}catch(TokenExpiredException ex){
log.error("[Auth]JWTtoken is expired");
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.setStatus(HttpStatus.UNAUTHORIZED.value());

PrintWriter out = response.getWriter();
JsonObject json = new JsonObject();
json.addProperty("error", "JWT error");
json.addProperty("error_deatils", "JWT token is expired");
out.print(json);
out.flush();
return;
}catch(JWTVerificationException ex){
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.setStatus(HttpStatus.BAD_REQUEST.value());

PrintWriter out = response.getWriter();
JsonObject json = new JsonObject();
json.addProperty("error", "JWT error");
json.addProperty("error_deatils", "JWT Format is not unvalid");
out.print(json);
out.flush();
return;
}

Optional<Account> user = accountService.findUserByEmailOrNull(verify_result.getUser());
if(!user.isPresent()){
log.error(String.format("[Auth]JWT Token is passed. but %s is not exist", verify_result.getUser()));
throw new UsernameNotFoundException(String.format("%s is not exist. but try login", verify_result.getUser()));
}

// 스프링시큐리티 인증정보 초기화(인가설정 적용을 위해)
PrincipalDetails principalDetails = new PrincipalDetails(user.get());
UsernamePasswordAuthenticationToken auth_token = new UsernamePasswordAuthenticationToken(
principalDetails,
null,
principalDetails.getAuthorities()
);
SecurityContextHolder.getContext().setAuthentication(auth_token);

filterChain.doFilter(request, response);
}
}
Loading

0 comments on commit 8abdd9c

Please sign in to comment.