[Spring Boot] Spring Security, JWT

류슬기·2021년 3월 17일
0

TIL

목록 보기
6/16
post-thumbnail

Spring boot + react 연동 해 준 후 진행
이전글 보기

Spring Security

Spring Security는 Login과 관련하여 표준화해버린 것을 말한다.
Spring Boot는 Framework이기 때문에 이러한 특성을 가질 수 있다.

// pom.xml
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
	<groupId>io.jsonwebtoken</groupId>
	<artifactId>jjwt</artifactId>
	<version>0.9.1</version>
</dependency>

2개의 플러그인을 넣어준 후 실행하면
다음과 같이 security password가 나타난다.

8080으로 접속하면 이전에 연결해둔 react 화면이 아니라 로그인 화면이 뜬다.

여기에 실행할 때 뜬 password를 넣어주면
플러그인이 오류없이 실행된 것을 확인 할 수 있다.

JWT(Json Web Tokens)

필요한 정보를 토큰에 저장(클라이언트에 저장)해서 이를 증명서 처럼 사용 하는 것이다.

API

가장 대표적인 두 가지 방식 SOAP, REST

예전에는 Session, Cookie를 사용하는 시큐어 코딩(Secure Coding) 을 하였지만 보안이 뚫려서 이제는 JWT를 사용한다.

SOAP(Simple Object Access Protocol) 프로토콜 → 예전방식

  • stateless 프로토콜기반
  • Xml 방식
  • 예) 공인인증서

REST(Representational State Transfer) 아키텍쳐 스타일

  • stateful
  • Json의 대표상태 = Key
  • payload
      payload        vs          parameter
이기종끼리 데이터 전달가능    같은 언어 내에서 데이터 전달 가능

토큰을 클라이언트에 담아서 보내면
서버에서 토큰이 일치하는지 확인하고 로그인이 성공함

payload

https://aonee.tistory.com/70 참고

왼쪽에 보이는 것이 Serialization된 모습
header.body.signature

  • header : token이 들어 있음
  • payload - claim(entity, dto와 일치)을 포함한다

    아직 페이로드가 무엇인지 정확하게 이해되지 않음
    Request react에서 server로 보내는 객체이름 (Json)형태 body
    토큰은 헤더에 담겨서 request한다.
    axios.post(url, Json)

Design Patterns - Adapter Pattern

한 클래스의 인터페이스(JAVA)를 클라이언트(JS)에서 사용하고자하는 다른 인터페이스로 변환한다.
어댑터를 이용하면 인터페이스 호환성 문제 때문에 같이 쓸 수 없는 클래스(Token)들을 연결해서 쓸 수 있다.

@Configuration

자체 로그인 시스템 보안을 구현하기 위해 WebSecurityConfigurerAdapter을 상속

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	
    @Autowired JwtTokenProvider jwtTokenProvider;
    // p(protocal이니깐 soap방식, 무력화시키기)
    // soap과 rest 2가지 방식을 같이 사용 할 수 없기 때문
    http.httpBasic().disable()
    	// Cross Site Request Forgery : 다른 해커가 와서 하는 공격, 근데 뚫려서 이제 사용 안함
        .csrf().disable()
 	// 스프링시큐리티가 생성하지도않고 기존것을 사용하지도 않음
    	.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        // 새로운 내용으로 변경된다
        .and()
           .authorizeRequests()
           // ant표기법 사용
           // controller에 postmapping해둔 경로(여기로 접근하면 모두 승인)
           .antMatchers("/*/login", "/*/signup").permitAll
           // 그외 나머지 요청은 모두 인증된 회원만 접근 가능
           .anyRequest().hasRole("USER")
        .and()
           // 진입점
           .exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint())
        .and()
           // 위에 내용들 다 만족하면 token제공
           .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class);		
   }
}

ant표기법 http://utils.egloos.com/v/3011811
1) * : 0개 또는 그이상의 글자가 매칭
2) ? : 한글자와 매칭
3) ** : 다계층을 나타냄 예) dir//A => dir/dir1/A, dir/dir2/A, dir/dir1/dirA/A 다 포함.
4) / 또는 \ 으로 끝날 경우 : / 또는 ** 과 동일

CustomAuthenticationEntryPoint

인증과정에서 실패하거나 인증헤더(Authorization)를 보내지 않게되는 경우
401(UnAuthorized) 응답값을 받게된다.

@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint{

	@Override
	public void commence(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException authException) throws IOException, ServletException {
		response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied");
		// 권한이 없을 경우 알려주는.. 401 에러	
	}
}

JwtAuthenticationFilter(미완료)

사용자정의이기때문에 직접 만들어줌

@AllArgsConstructor @Data @Component
public class JwtAuthenticationFilter extends GenericFilterBean{
	private JwtTokenProvider JwtTokenProvider;
	
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
	}
}

JwtTokenProvider

Token을 생성하고 유효성 검증

@RequiredArgsConstructor
@Component
public class JwtTokenProvider {
	
    // 외부에서 기본값을 주는 공간
    @Value("spring.jwt.secret")
    private String SECRET_KEY;
    // 유효시간 1시간
    private long tokenValidMilisecond = 1000L * 60 * 60;
    private final UserDetailsService userDetailsService;
    
    // 생성 이후에 SECRET_KEY를 만들어라
    @PostConstruct
    protected void init() {
    	SECRET_KEY = Base64.getEncoder().encodeToString(SECRET_KEY.getBytes());
    }
    
    // token 생성
    public String createToken(String userPk, Collection<? extends GrantedAuthority> roles) {
    	Claims claims = Jwts.claims().setSubject(userPk);
        // 형태가 같기 때문에 HashMap으로 보내면 Json으로 받음
    	claims.put("roles", roles);
    	
    	Date now  = new Date();
    	
    	return Jwts.builder()
        	// 데이터
    		.setClaims(claims)
            	// 토큰 발행날짜
    		.setIssuedAt(now)
    		.setExpiration(new Date(now.getTime() + tokenValidMilisecond))
    		.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
            	// 압축
    		.compact();
    }
}
  • Collection vs Collections

    • Collection = interface
    • Collections = class
  • 와일드 카드

<T> 일반 generic 한 종류를 polymophism
<?> 자손까지 polymophism

spring.jwt.secret 추가

// properties.yml
 jwt:
    secret: jwtSecret

@Controller(미완료)

@RestController
@RequestMapping(value ="/auth")
public class AuthController {
	
	@PostMapping("/login")
	public void login(@RequestBody UserDto user) {
		// Map<String, Object> map = new HashMap<>();
	}
}
profile
FE Developer🌱

0개의 댓글