Spring boot + react 연동 해 준 후 진행
이전글 보기
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를 넣어주면
플러그인이 오류없이 실행된 것을 확인 할 수 있다.
필요한 정보를 토큰에 저장(클라이언트에 저장)해서 이를 증명서 처럼 사용 하는 것이다.
가장 대표적인 두 가지 방식 SOAP, REST
예전에는 Session, Cookie를 사용하는 시큐어 코딩(Secure Coding) 을 하였지만 보안이 뚫려서 이제는 JWT를 사용한다.
payload vs parameter
이기종끼리 데이터 전달가능 같은 언어 내에서 데이터 전달 가능
토큰을 클라이언트에 담아서 보내면
서버에서 토큰이 일치하는지 확인하고 로그인이 성공함
https://aonee.tistory.com/70 참고
왼쪽에 보이는 것이 Serialization된 모습
header.body.signature
아직 페이로드가 무엇인지 정확하게 이해되지 않음
Request react에서 server로 보내는 객체이름 (Json)형태 body
토큰은 헤더에 담겨서 request한다.
axios.post(url, Json)
한 클래스의 인터페이스(JAVA)를 클라이언트(JS)에서 사용하고자하는 다른 인터페이스로 변환한다.
어댑터를 이용하면 인터페이스 호환성 문제 때문에 같이 쓸 수 없는 클래스(Token)들을 연결해서 쓸 수 있다.
자체 로그인 시스템 보안을 구현하기 위해 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) / 또는 \ 으로 끝날 경우 : / 또는 ** 과 동일
인증과정에서 실패하거나 인증헤더(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 에러
}
}
사용자정의이기때문에 직접 만들어줌
@AllArgsConstructor @Data @Component
public class JwtAuthenticationFilter extends GenericFilterBean{
private JwtTokenProvider JwtTokenProvider;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
}
}
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
와일드 카드
<T> 일반 generic 한 종류를 polymophism
<?> 자손까지 polymophism
// properties.yml
jwt:
secret: jwtSecret
@RestController
@RequestMapping(value ="/auth")
public class AuthController {
@PostMapping("/login")
public void login(@RequestBody UserDto user) {
// Map<String, Object> map = new HashMap<>();
}
}