稲妻市場V 2-(2)JWTトークン



1.SecurityContextPersistentFilter
2.LogoutFilter
3.UsernamePasswordAuthenticationFilter
4.DefaultLoginPageGenerationFilter
5.BasicAuthenticationFilter
6.RememberMeAuthenticationFilter
7.AnonymousAuthenticationFilter
8.SessionManagementFilter
9.ExceptionTranslationFilter
10.FilterSecurityInterceptor
3番目のU s e r m e P a s s s w o r d AuthenticationFilterの前に、jwt関連フィルタを加えてトークンに分け、ユーザーをSecurityContextHolderに入れるか、異常処理を行います.

トークンの作成


ログインに成功したら、コインを提供します.
  • JwtTokenProvider
    トークンを生成する方法.トークンは、ユーザのid、emailをclaimに入れ、後で作成するAPIに使用します.
  • // JWT 토큰 생성
        public String createToken(Long userIdx,String email) {
            Date now = new Date();
            return Jwts.builder()
                    .claim("userIDx", userIdx)
                    .claim("email", email)// 정보 저장
                    .setIssuedAt(now) // 토큰 발행 시간 정보
                    .setExpiration(new Date(now.getTime() + tokenValidTime)) // set Expire Time
                    .signWith(SignatureAlgorithm.HS256, secretKey)  // 사용할 암호화 알고리즘과 signature 에 들어갈 secret값 세팅
                    .compact();
        }

    タグから情報を抽出


    以下のpermitAll()で提供されるAPIのほか、トークンも必要です.
    会員登録や登録時にはトークンがないため、開く必要がありますが、Mypageのような場合はトークンが必要です.この設定は、次のWebSecurityConfigurator Adapterで変更できます.
  • WebSecurityConfig (extends WebSecurityConfigurerAdapter)
  • @Override
        protected void configure(HttpSecurity http) throws Exception {
    
            http.httpBasic().disable()
                    .csrf().ignoringAntMatchers("/h2-console/**").disable()
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .authorizeRequests()
                    .antMatchers("/h2-console/**").permitAll()
                    .antMatchers("/user/**").permitAll()
                    .antMatchers("/category/**").permitAll()
                    .anyRequest().authenticated()
                    .and()
                    .exceptionHandling()
                    .authenticationEntryPoint(authenticationEntryPoint)
                    .accessDeniedHandler(accessDeniedHandler)
                    .and()
                    .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider),
                            UsernamePasswordAuthenticationFilter.class);
        }
    Spring Securityは、図のように上のフィルタチェーンでログインします.JWTを使用したログインを処理するには、U s e r m e P a s s s s w o r d AuthenticationFilterの前にJWT処理に関連するフィルタを挿入します.
  • JwtAuthenticationFilter(extends GenericFilter)
  • @RequiredArgsConstructor
    public class JwtAuthenticationFilter extends GenericFilter {
    
        private final JwtTokenProvider jwtTokenProvider;
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            // 헤더에서 JWT 를 받아옵니다.
            String token = jwtTokenProvider.resolveToken((HttpServletRequest) servletRequest);
    
            // 유효한 토큰인지 확인합니다.
            if (token != null && jwtTokenProvider.validateToken(servletRequest,token)) {
                try {
                    // 토큰이 유효하면 토큰으로부터 유저 정보를 받아옵니다.
                    Authentication authentication = jwtTokenProvider.getAuthentication(token);
                    // SecurityContext 에 Authentication 객체를 저장합니다.
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                } catch (UsernameNotFoundException e) {
                    servletRequest.setAttribute("exception","UsernameNotFoundException");
                }
            }
    
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }
    Headerにタグを追加してAPIを呼び出す場合、タグから情報を抽出したり、タグを検証したりする方法が必要になります.
    前述したように、リーダにX-AUTH-TOCKENを挿入してAPIを呼び出す場合
    Headerからトークンを抽出する方法.トークンがない場合やトークンがない場合は例外処理を行います.
  • JwtTokenProvider
  • 前述したように、リーダにX-AUTH-TOCKENを挿入してAPIを呼び出す場合
    Headerからトークンを抽出する方法.トークンがない場合やトークンがない場合は例外処理を行います.
        public String resolveToken(HttpServletRequest request) {
            String token = request.getHeader("X-AUTH-TOKEN");
            if (token == null || token.isEmpty()) {
                request.setAttribute("exception","NotFoundToken");
            }
            return token;
        }
    コインを確認する必要があります.次のタグが無効な場合は、フィルタで例外処理を行います.
    (1)トークンなし
    (2)フラグが一致しない
    (3)トークンの期限切れ
    (4)トークン検証後の権限拒否
    public boolean validateToken(ServletRequest request, String jwtToken) {
    
            try {
                Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwtToken);
                return true;
            } catch (MalformedJwtException e) {
                request.setAttribute("exception","MalformedJwtException");
            } catch (ExpiredJwtException e) {
                request.setAttribute("exception","ExpiredJwtException");
            } catch (UnsupportedJwtException e) {
                request.setAttribute("exception","UnsupportedJwtException");
            } catch (IllegalArgumentException e) {
                request.setAttribute("exception","IllegalArgumentException");
            } catch (SignatureException e) {
                request.setAttribute("exception", "SignatureException");
            }
            return false;
        }
    トークングループに保存されたEメールをUserDetailsServiceに送信し、ユーザーを検索してSecurityContextHolderに送信します.
    public Authentication getAuthentication(String token) {
            UserDetails userDetails = userDetailsService.loadUserByUsername(this.getUserPk(token));
            return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
        }
        
    public String getUserPk(String token) {
            return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().get("email", String.class);
        }
  • CustomUserDetailService (implements UserDetailsService)
  • @RequiredArgsConstructor
    @Service
    public class CustomUserDetailService implements UserDetailsService {
    
        private final UserRepository userRepository;
    
        @Override
        public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
            return userRepository.findByEmail(email).orElseThrow(() -> new UsernameNotFoundException("NOT FOUND USER"));
        }
    }