[Spring] Spring Security - JWT


🤖 Spirng Security?


強力なユーザー認証およびアクセス制御framework.Javaアプリケーションの認証と認可を提供し、複数のフィルタを使用してカスタマイズできることに重点を置いています.

用語


「認証は、マスターの認証プロセスです.」この主張を検証する過程だ.

  • 本体(Principal)
    一般的にはプレイヤー(ユーザ)を指し、主体は身分証明書情報、すなわち証明書を提示し、自分を認証することを要求する.本体がプレイヤーの場合、パスワードは通常パスワードです.

  • 権限(権限、権限)
    これは、認証を完了したユーザーに権限を付与し、ターゲット・アプリケーションの特定のリソースにユーザーがアクセスできるようにするプロセスです.承認は認証プロセスの後に行わなければなりません.一般的にはロールとして権限を付与します.

  • アクセス制御
    アプリケーションリソースにアクセスする動作を制御します.したがって、どのユーザがどのリソースにアクセスできるかを決定する動作、すなわちアクセス制御決定.リソースのアクセス属性とユーザーに付与された権限またはその他の属性を比較することによって決定されます.
  • 🪴 FilterChain


  • Spring Securityは標準のSebritフィルタを使用します.

  • 他の要求と同様に、HttpServletRequestおよびHttpServletResponseが使用される.

  • Spring Securityは、サービスに応じて内部構成フィルタを設定します.各フィルタにはそれぞれの役割があり,フィルタ間には依存関係があるため,順序が重要である.

  • XMLタグのネーミングスペース構成を使用すると、フィルタは自動的に構成されますが、ネーミングスペース構成でサポートされていない機能を使用する必要がある場合や、カスタムフィルタを使用する必要がある場合は、空を明示的に登録できます.
  • クライアントが要求を発行すると、DelegatingFilterProxyは要求をブロックし、Spring Securityの空き場所に送信する.(DispatcherServiceletの前に実行します.)
    このDeletgatingFilterProxyはwebです.xmlとアプリケーションContextのリンクを指定します.
    したがって、DelegatingFilterProxyは、Springのアプリケーションコンテキストで得られたFilter Beanの代わりに一言を用いる.
    だからこのbeanはjavaxです.servlet.Filterを実装する必要があります.この記事では、jwtAuthenticationFilterになります.(次のソースコードを参照)
    Spring Security Filter Chainは以下の通りです.

  • SecurityContextPersistentFilter :
    セキュリティディレクトリRepositoryは、セキュリティディレクトリをインポートし、セキュリティディレクトリHolderに注入することができます.逆も同様です.

  • LogoutFilter :
    ログアウト要求を監視し、要求時に「認証主体」(Principal)をログアウトします.

  • UsernamePasswordAuthenticationFilter :
    ログイン要求を監視し、認証を行います.

  • DefaultLoginPageGenerationFilter :
    ユーザーが個別のログインページを実装していない場合は、スプリングのデフォルト設定のログインページに移動できます.

  • BasicAuthenticationFilter :
    HTTPリクエストの(BASIC)検証ヘッダを処理し、結果をSecurityContextHolderに保存します.

  • RememberMeAuthenticationFilter :
    securityContextに「認証」(Authentication)オブジェクトが存在し、「RememberMeServices」が実装されたオブジェクト要求が存在する場合、RememberMeは認証トークンとしてコンテキストに注入される.

  • AnonymousAuthenticationFilter :
    このフィルタを呼び出す前にユーザー情報が検証されていない場合は、匿名のユーザーとみなされます.

  • SessionManagementFilter :
    リクエストが開始してから許可されているユーザーであることを確認します.許可されているユーザーである場合は、セッション固定保護メカニズムを有効にするためにSession AuthenticationStrategyを呼び出したり、複数の同時ログインの検証など、セッションに関連するアクティビティを実行したりします.

  • ExceptionTranslationFilter :
    フィルタチェーンで発生したすべての例外を処理します.

  • FilterSecurityInterceptor :
    認証処理をAccessDecisionManagerに委任し、HTTPリソースに対してセキュリティ処理を実行します.
  • 🏰 Spring Security構造



  • ユーザーが入力したユーザー情報を使用して認証を要求します.(Request)

  • AuthenticationFilterはそれをブロックして、Username PasswordAuthenticationToken(認証用のオブジェクト)を生成します.

  • フィルタは要求を処理し、認証とU s e r m e P a s s s s s w o r d AuthenticationTokenをAuthenticationManagerのインプリメンテーションProviderManagerに渡します.

  • AuthenticationManagerは認証とU s e r m e P a s s s s s s wordAuthenticationTokenをAuthenticationProviderに渡して検証します.

  • 次に、ユーザー情報をUserDetails Serviceに渡し、データベース内のユーザー認証情報と比較します.

  • DBで見つけたユーザー情報UserDetailsオブジェクトを作成します.

  • AuthenticationProviderはUserDetailsを受信し比較する.

  • 認証が完了すると、権限とユーザー情報を含む認証オブジェクトが返されます.

  • 認証情報をAuthenticationFilterに送信します.

  • 認証をSecurityContextに保存します.
  • 認証情報は、最終的に認証オブジェクトをSecurityContextHolderセッション領域のSecurityContextに格納する.セッションにユーザ情報を格納することは、従来のセッション-Cookieベースの認証方法を使用することを意味する.

    🤡 JWT(Json Web Token)


    JWT(Json Web Token)は、通常、クライアントとサーバ、サービス、サービスとの間で通信する際に許可されるトークンであるWeb標準(RFC 7519)である.
    HEADER.PAYLOAD.SIGNATURE
    通常、ヘッダー、ペイロード、署名の3つの部分からなる点分割構造.
    ヘッダはトークンタイプとハッシュアルゴリズムを格納します.
    ペイロードには実際に伝達された情報が含まれ、署名には上記の改ざんを防止する値が含まれている.

    ユーザ認証が完了すると、サーバ側は、JWTトークンをBodyに装着してクライアントに渡し、要求されたAPIサーバのヘッダにJWTトークンを装着して要求することで、認証ユーザにリソースを提供することができる.

    🧞‍♂️ 例


    開発環境


    gradle
    Java 8
    build.gradle
    dependencies {
        .
        .
        implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
        implementation 'org.springframework.boot:spring-boot-starter-security'
        implementation group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.1'
        .
        .
        .
    }
    まず必要なライブラリを受け入れ、ドメインオブジェクトを作成します.
    User.java
    @Builder
    @Getter
    @Setter
    @AllArgsConstructor
    @NoArgsConstructor
    @ToString
    @Entity
    @EntityListeners(AuditingEntityListener.class)
    @Table(name="USERS")
    public class User{
    
        @Id
        @Column(name = "user_id")
        private String userId;
    
        @Column(name = "user_name")
        private String userName;
    
        @Column(name = "user_pwd")
        private String userPwd;
    
        @Column
        private String company;
    
        @Column
        private String position;
    }
    その後、データベースアクセス時に使用するRepositoryとServiceが作成されます.(サービスは後で作成できます.)
    UserService.java
    UserServiceでは、それぞれ認証ドメインとAPIのために作成されているそうです.
    △ここは線を1本しか作らない.
    @Service
    public class UserService {
    
        @Autowired
        UserRepository userRepository;
    
        .
        .
        .
        
        public Optional<User> findByIdPw(String id) {
        	return userRepository.findById(id);
        }
    
        .
        .
    }
    後にSpring Security Filter Chainを使用することを明記します.
    まずWebSecurityConfigurerAdapterを継承する構成オブジェクトを作成し、そこに@EnableWebSecurity宣言を追加すればよい.
    SecurityConfiguration.java
    @Slf4j
    @EnableWebSecurity
    @RequiredArgsConstructor
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private final JwtAuthenticationEntryPoint unauthorizedHandler;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .cors() //(1)
                .and()
                .csrf() //(2)
                .disable()
                .exceptionHandling() //(3)
                .authenticationEntryPoint(unauthorizedHandler)
                .and()
                .sessionManagement() //(4)
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests() // (5)
                .antMatchers(RestControllerBase.API_URI_PREFIX + "/auth/**")
                .permitAll()
                .antMatchers(RestControllerBase.API_URI_PREFIX + "/**")
                .authenticated()
                .and()
                .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .formLogin().disable().headers().frameOptions().disable();
                
            http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
        }
        
        @Bean
        public CorsConfigurationSource corsConfigurationSource() {
            CorsConfiguration configuration = new CorsConfiguration();
            configuration.addAllowedOrigin("*");
            configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE"));
            configuration.addAllowedHeader("*");
            configuration.setAllowCredentials(true);
            configuration.setMaxAge(3600L);
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/**", configuration);
            return source;
        }
        
        //비밀번호 암호화를 위한 Encoder 설정
        @Bean
        public BCryptPasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
     }
    (1)クロスソースリソース共有設定.反応があるならjsをクライアントとして使用する場合、ポート番号は異なります.
    だからCorsConfiguration Source Beanで
  • addAllowedOrigin(アクセス制御Allowe-Origin)すべてのソース
  • を許可
  • setAllowedMethods上記要求方式
  • の使用を許可する.
  • アクセス制御を許可するすべての方法
    (2)CSRF(Cross Site Request Forgery)サイト間で設定の偽造を要求する.この設定は複雑で、REST APIサーバのみに使用されるため、disableとして処理される.
    (3)認証・許可エラーの共通処理の部分.

    🐝 Spring Security JWT処理手順



    1. JwtAuthFilter

  • このフィルタは、すべてのアクセスサーバのAPIのJWTトークンに対して有効性チェックを行う.(ex.コイン偽造、有効期間等)
  • 検査を行った後、コントローラに送信されます.
  • APIでは、ログイン前に許可しなければならないAPIに対して、FilterSkipMatcherが例外処理を行います.(登録、会員登録ページ、CSS、...)
  • ログイン処理は、JWTが生成される前に行われるため、FormLoginFilterでアクセスできる例外処理である.
  • 2. FormLoginFilter

  • で転送されたユーザ名およびパスワードがDBと比較的正確である場合、JWTが生成され、応答に含まれる.
  • 3. Client

  • クライアント応答のJWTをローカルまたはCookieに保存します.
  • の後、要求サーバのAPIは、JWTトークンをヘッダに含めるように招待する.
  • 4. JwtAuthFilter


    ヘッダを含むJWTを検証すると、
  • 要求のAPIが許可されます.
  • を許可しながら、ログイン情報を生成し、コントローラに移動する.(UserDetailsImpl)
  • 5. Controller


    検索
  • ユーザーが興味を持っている商品を登録して戻ります.