Spring SecurityおよびOAuth 2.0を使用したログイン(03)
58661 ワード
03.Googleログインのバインド
3-1)ユーザークラスの作成:担当ユーザー情報
domainでユーザーパッケージを作成する(domain/user/user)
package com.cutehuman.springboot.domain.user;
import com.cutehuman.springboot.domain.BaseTimeEntity;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Getter
@NoArgsConstructor
@Entity
public class User extends BaseTimeEntity {
@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) // 1.
@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)3-2)EnumクラスRoleの作成:ユーザー権限の管理
1. domin/user/Role
package com.cutehuman.springboot.domain.user;
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;
}
❗Spring Securityは、ROLEが常に権限コードの前にあることを要求するex)ROLE GUEST、ROLE USER等
2.ユーザーレポートインタフェースの作成:ユーザーがCRUDを担当する
package com.cutehuman.springboot.domain.user;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}
返される値にemailで生成されたユーザーがいるかどうか.
最初の登録ユーザーであることを確認するための
3-3)スプリング安全設定
1. build.スプリングの安全性に関連する依存項目をgradeに追加
compile('org.springframework.boot:spring-boot-starter-oauth2-client')
spring-boot-starter-oauth2-client2.OAuthライブラリを使用したソーシャルログイン設定コードの作成
config.authパッケージ作成:セキュリティに関連するすべてのクラスがここに含まれます.
SecurityConfigクラスの作成
import com.cutehuman.springboot.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 // 1.
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final CustomOAuth2UserService customOAuth2UserService;
@Override
protected void configure(HttpSecurity http) throws Exception{
http.csrf().disable()
.headers().frameOptions().disable() // 2.
.and().authorizeRequests() // 3.
.antMatchers("/", "/css/**", "/images/**",
"/js/**","h2-console/**").permitAll()
.antMatchers("/api/v1/**").hasRole(Role.USER.name()) // 4.
.anyRequest().authenticated() // 5.
.and().logout().logoutSuccessUrl("/") // 6.
.and().oauth2Login() // 7.
.userInfoEndpoint() // 8.
.userService(customOAuth2UserService); // 9.
}
}
1. @EnableWebSecurity2. csrf().disable().headers().frameOptions().disable()
3. authorizeRequests
antMatchersオプションを使用するには、
4. antMatchers
指定されたURL(例えば
アドレスが
5. anyRequest
6. logout().logoutSuccessUrl("/")
7. oauth2Login
8. userInfoEndpoint
9. userService
import com.cutehuman.springboot.domain.user.User;
import com.cutehuman.springboot.domain.user.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpSession;
import java.util.Collections;
@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<OAuth2UserRequest, OAuth2User>
delegate = new DefaultOAuth2UserService();
OAuth2User oAuth2User = delegate.loadUser(userRequest);
String registrationId = userRequest.getClientRegistration().getRegistrationId(); // 1.
String userNameAttributeName = userRequest.getClientRegistration()
.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName(); // 2.
OAuthAttributes attributes = OAuthAttributes.of(registrationId,
userNameAttributeName, oAuth2User.getAttributes()); // 3.
User user = saveOrUpdate(attributes);
httpSession.setAttribute("user", new SessionUser(user)); // 4.
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);
}
}
1. registrationIdGoogleにログインするかどうかを区別するために使用されます
2. userNameAttributeName
3. OAuthAttributes
4. SessionUser
+)Googleユーザー情報の更新時に使用するための更新機能も実装されている
ユーザー名またはプロファイルの写真が変更された場合、ユーザーエンティティにも
ユーザー・クラスではなく新しいクラスを作成および使用する理由
Failed to convert from type [java.lang.Object] to type [byte[]] for value 'com.~~.book.springboot.domain.user.User@4a43d6'
これは、ユーザー・クラスをシリアル化せずにセッションに保存することを意味します.ここではDtoでAuthAttributesを見るのでconfig.auth.dtoパッケージを作成し、そのパッケージに
import com.cutehuman.springboot.domain.user.Role;
import com.cutehuman.springboot.domain.user.User;
import lombok.Builder;
import lombok.Getter;
import java.util.Map;
@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;
}
// 1.
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();
}
// 2.
public User toEntity(){
return User.builder()
.name(name)
.email(email)
.picture(picture)
.role(Role.GUEST)
.build();
}
}
SessionUserは認証されたユーザ情報のみを必要とするため、name、email、およびpictureフィールド
import com.cutehuman.springboot.domain.user.User;
import lombok.Getter;
import java.io.Serializable;
@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();
}
}
3-4)テストログイン
1. index.「ひげ」画面にログインボタンを追加
スプリングの安全性の確認
...
<h1>스프링부트로 시작하는 웹 서비스 Ver.2</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>
{{#user}}
Logged in as: <span id="user">{{user}}</span>
<a href="/logout" class="btn btn-info active" role="button">Logout</a>
{{/user}}
{{^user}}
<a href="/oauth2/authorization/google" class="btn btn-success active" role="button">Google Login</a>
<a href="/oauth2/authorization/naver" class="btn btn-secondary active" role="button">Naver Login</a>
{{/user}}
</div>
</div>
<br>
<!--목록 출력 영역-->
...
(開発者はログアウトURLのコントローラxを作成する必要がある)
デフォルトのログインURL
2.IndexControllerにuserNameをモデルに格納するコードを追加する
index.UNAMEをひげに使えるようにする
@RequiredArgsConstructor
@Controller
public class IndexController {
private final PostsService postsService;
private final HttpSession httpSession;
@GetMapping("/")
public String index(Model model){ // 1.
model.addAttribute("posts", postsService.findAllDesc());
SessionUser user = (SessionUser) httpSession.getAttribute("user"); // 2.
if(user != null){ // 3.
model.addAttribute("user", user.getName());
}
return "index";
}
3.テスト
+) https://github.com/jojoldu/freelec-springboot2-webservice/issues/169
SELECT email, name, picture, role FROM USER
update user set role = 'USER';
セッションはGUEST情報として保存されています.ログアウトして再ログインし、セッション情報を最新情報に更新し、記事を登録してください.
Reference
この問題について(Spring SecurityおよびOAuth 2.0を使用したログイン(03)), 我々は、より多くの情報をここで見つけました https://velog.io/@cutehuman/05-스프링-시큐리티와-OAuth-2.0으로-로그인-기능-구현하기03テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol