Googleログインとスプリングのセキュリティ設定を有効にする


このプレゼンテーションは、Spring BootとAWSが独自にWebサービスマニュアルに基づいて作成したものです.

1.ドメインユーザークラスの作成


User

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Getter
@NoArgsConstructor
@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String email;

    @Column
    private String picture;

    @Enumerated(EnumType.STRING) ⓐ
    @Column(nullable = false)
    private Role role;

    @Builder
    public User(String name, String email, String picture, Role role) {
        this.name = name;
        this.email = email;
        this.picture = picture;
        this.role = role;
    }

    public User update(String name, String picture) {
        this.name = name;
        this.picture = picture;

        return this;
    }

    public String getRoleKey() {
        return this.role.getKey();
    }
}
  • ⓐ @Enumerated(EnumType.STRING)
  • JPAを使用してデータベースを格納場合、Enum値を格納する理由を決定する
  • .
  • デフォルト記憶整数
  • の数字が格納されている場合、データベース内の値がどのコードを意味するかは判断できないため、宣言は文字列
  • として格納することができる.

    Role

  • ユーザ権限を管理するEnumクラス
  • import lombok.Getter;
    import lombok.RequiredArgsConstructor;
    
    @Getter
    @RequiredArgsConstructor
    public enum Role {
    
        GUEST("ROLE_GUEST", "손님"),
        USER("ROLE_USER", "일반 사용자");
    
        private final String key;
        private final String title;
    }
  • スプリングパリティでは、パーミッションコードに常にROLEがある必要があります.合計
  • UserRepository

    import org.springframework.data.jpa.repository.JpaRepository;
    
    import java.util.Optional;
    
    public interface UserRepository extends JpaRepository<User, Long> {
        
        Optional<User> findByEmail(String email);
        
    }

    2.スプリング安全性の設定


    2.1 build.grade依存性の追加

    implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
  • spring-boot-starter-oauth2-client
  • ソーシャルログインなどのクライアントのソーシャル機能への依存性
  • デフォルト管理
  • spring-security-oauth 2-clientおよびspring-security-oauth 2-jose
  • 2.2 config.authパッケージの作成

  • セキュリティクラス
  • 2.2.1 SecurithConfig

    import com.study.aws.studyspringbootaws.domain.user.Role;
    import lombok.RequiredArgsConstructor;
    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;
    
    @RequiredArgsConstructor
    @EnableWebSecurity ⓐ
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        private final CustomOAuth2UserService customOAuth2UserService;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()
                    .headers().frameOptions().disable() ⓑ
                    .and()
                        .authorizeRequests() ⓒ
                        .antMatchers("/", "/css/**", "/images/**", "/js/**", "/h2-console/**")
                            .permitAll()
                        .antMatchers("/api/v1/**") ⓓ
                            .hasRole(Role.USER.name())
                        .anyRequest() ⓔ
                            .authenticated()
                    .and()
                        .logout()
                            .logoutSuccessUrl("/") ⓕ
                    .and()
                        .oauth2Login() ⓖ
                            .userInfoEndpoint() ⓗ
                                .userService(customOAuth2UserService) ⓘ
                    ;
        }
    }
  • ⓐ @EnableWebSecurity
  • Spring Security設定を有効にする
  • ⓑ csrf().disable().headers().frameOptions().disable()
  • h 2-コンソール画面
  • を使用する処理オプションを無効にする
  • ⓒ authorizeRequests()
    オプション
  • URL固有の権限管理の開始点
  • を設定する.
    antMatchersオプションを使用するには、
  • authorizeRequestsを宣言する必要があります.
  • ⓓ antMatchers("/api/v1/**")
  • 権限管理オプション
  • URL、HTTP方法で
  • を管理できる
  • permitAll()オプションを使用して、完全権限
  • を付与
  • hasRole()オプションで、
  • へのユーザーのアクセスを許可します.
  • ⓔ anyRequest()
  • は、設定値以外のURL
  • を示す.
  • 認証()を追加し、他のすべてのURLが認証ユーザによって
  • を使用できるようにする.
  • 認証ユーザ、すなわちログインユーザ
  • .
  • ⓕ logout().logoutSuccessUrl("/")
  • ログアウト機能の複数の設定エントリポイント
  • ログアウトに成功すると、"/"アドレス
  • が返されます.
  • ⓖ oauth2Login()
  • OAuth 2登録機能の冗長設定エントリポイント
  • ⓗ userInfoEndpoint()
  • OAuth 2ログイン成功後のユーザ情報入力設定
  • 特定のユーザー・サービス
  • ユーザー・サービス・インタフェースのインプリメンテーションを登録し、ソーシャル・ログインが成功したときに後続の操作を実行する
  • リソースサーバ(すなわち、ソーシャルサービス)は、ユーザ情報を取得する場合に実行すべき他の機能
  • を指定することができる.

    2.2.2 CustomOAuth2UserService

  • は、ユーザーがGoogleからログインして取得した情報に基づいて、情報の購読や変更、セッションの保存などの機能をサポートします.
    @RequiredArgsConstructor
    @Service
    public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
    
        private final UserRepository userRepository;
        private final HttpSession httpSession;
    
        @Override
        public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
            OAuth2UserService delegate = new DefaultOAuth2UserService();
            OAuth2User oAuth2User = delegate.loadUser(userRequest);
    
            String registrationId = userRequest.getClientRegistration().getRegistrationId(); ⓐ
            String userNameAttributeName =
                    userRequest.getClientRegistration()
                            .getProviderDetails()
                            .getUserInfoEndpoint()
                            .getUserNameAttributeName(); ⓑ
    
            OAuthAttributes attributes = OAuthAttributes.of(registrationId,
                    userNameAttributeName,
                    oAuth2User.getAttributes()); ⓒ
    
            User user = saveOrUpdate(attributes);
    
            httpSession.setAttribute("user", new SessionUser(user)); ⓓ
    
            return new DefaultOAuth2User(
                    Collections.singleton(new SimpleGrantedAuthority(user.getRoleKey())),
                    attributes.getAttributes(),
                    attributes.getNameAttributeKey()
            );
        }
    
        private User saveOrUpdate(OAuthAttributes attributes) {
            User user = userRepository.findByEmail(attributes.getEmail())
                    .map(entity -> entity.update(attributes.getName(),
                            attributes.getPicture()))
                    .orElse(attributes.toEntity());
    
            return userRepository.save(user);
        }
    }
  • ⓐ registrationId
  • コード
  • 現在ログイン中のサービスを区別する
  • 現在、上記のコードはGoogleのみを使用しているため不要な値であるが、他のログイン連動時に他のサービス(ex.NAVERログイン、Google、KACAのID値を検証するための)であるかどうかを区別するための
  • である.
  • ⓑ userNameAttributeName
  • OAuth 2登録に必要なフィールド値(プライマリキーと同じ)
  • Google基本サポートコードですが、NAVERKACAなどはサポートされていません!Googleの基本コードは「sub」
  • 以降は、NAVERログインとGoogleログインを同時にサポートする場合に
  • を使用します.
  • ⓒ OAuthAttributes
  • Auth 2 UserServiceクラス、インポートされたAuth 2 Userプロパティを含む
  • 以降、Naverなどの他のソーシャルサイトもこのようなサイト
  • を使用している.
  • ⓓ SessionUser
  • セッションにおいてユーザ情報を格納DTOクラス
  • 2.2.3 OAuthAttributes


    作成
  • DTOパッケージ作成クラス
  • @Getter
    public class OAuthAttributes {
    
        private Map<String, Object> attributes;
        private String nameAttributeKey;
        private String name;
        private String email;
        private String picture;
    
        @Builder
        public OAuthAttributes(Map<String, Object> attributes,
                               String nameAttributeKey, String name,
                               String email, String picture) {
            this.attributes = attributes;
            this.nameAttributeKey = nameAttributeKey;
            this.name = name;
            this.email = email;
            this.picture = picture;
        }
    
        public static OAuthAttributes of(String registrationId,
                                         String userNameAttributeName,
                                         Map<String, Object> attributes) { ⓐ
            return ofGoogle(userNameAttributeName, attributes);
    
        }
    
        private static OAuthAttributes ofGoogle(String userNameAttributeName,
                                                Map<String, Object> attributes) { 
            return OAuthAttributes.builder()
                    .name((String) attributes.get("name"))
                    .email((String) attributes.get("email"))
                    .picture((String) attributes.get("picture"))
                    .attributes(attributes)
                    .nameAttributeKey(userNameAttributeName)
                    .build()
                    ;
        }
    
        public User toEntity() { ⓑ
            return User.builder()
                    .name(name)
                    .email(email)
                    .picture(picture)
                    .role(Role.GUEST)
                    .build();
        }
    }
  • ⓐ of()
  • Auth 2 Userが返すユーザ情報はMapであるため、各値
  • を変換する必要がある.
  • ⓑ toEntity()
    作成
  • ユーザーエンティティ
  • アイデンティティー属性からエンティティを作成する場合は、初回登録時の
  • である.
  • 登録時のデフォルト権限をGUIとするために、キャラクタビルダー値はRoleとなります.GUEST値
  • を提供
  • AuthAttributesクラスの作成が完了した場合、同じパッケージにSessionUserクラス
  • が作成されます.

    2.2.4 SessionUser

    @Getter
    public class SessionUser implements Serializable {
    
        private String name;
        private String email;
        private String picture;
    
        public SessionUser(User user) {
            this.name = user.getName();
            this.email = user.getEmail();
            this.picture = user.getPicture();
        }
    }

    2.2.5なぜUserクラスを使用しないで単独でSessionUserを作成しますか?

  • セッションに格納するには、クラスのシリアル化が必要です.
  • ユーザクラスはエンティティであるため、シリアル化が困難である
  • エンティティークラスは、他のエンティティーとの関係がいつ確立されるか分からない
  • exなどのサブエンティティ@OneToManyまたは@MantoManyを持っている場合、シリアル・ターゲットにもエンティティが含まれているため、パフォーマンスの問題や負の影響を及ぼす可能性があります.
  • 3.テストログイン


    3.1ログイン機能を画面に追加する


    3.1.1 index.mustache

    <h1>스프링 부트로 시작하는 웹 서비스</h1>
    <div class="col-md-12">
        <div class="row">
            <div class="col-md-6">
                <a href="/posts/save" role="button" class="btn btn-primary">글 등록</a>
                {{#userName}} ⓐ
                    Logged in as : <span id="user">{{userName}}</span>
                    <a href="/logout" class="btn btn-info active" role="button">Logout</a> ⓑ
                {{/userName}}
                {{^userName}} ⓒ
                    <a href="/oauth2/authorization/google" class="btn btn-info active" role="button">Google Login</a> ⓓ
                {{/userName}}
            </div>
        </div>
    </div>
  • ⓐ {{#userName}}
  • マストリッチは是非だけを判断し、なければ
  • は、常に最終値
  • を超える必要があります.
  • ⓑ a href="/logout"
    標準
  • の登録抹消URL
  • 、すなわち、開発者は、低URLのコントローラ
  • を作成する必要がない.
  • SecurityConfigクラス変更可能URL
  • ⓒ {{^userName}}
  • マストリッチ使用^この値が存在しない場合、使用^
  • ⓓ a href="/oauth2/authorization/google"
  • Spring Securityデフォルトで提供されるGoogleログインURL
  • 3.1.2 indexController

    
        private final HttpSession httpSession;
    
        @GetMapping("/")
        public String index(Model model) {
            model.addAttribute("posts", postsService.findAllDesc());
    
            SessionUser user = (SessionUser) httpSession.getAttribute("user"); 
    
            if (user != null) { 
                model.addAttribute("userName", user.getName());
            }
            return "index";
        }

    3.2プロジェクトの実行とテスト

  • ログインボタン画面
  • Googleログイン
  • チェック
  • 登録情報DB
  • 投稿登録(GUEST状態)

  • 権限を変更して登録