반응형
🤍스프링 시큐리티(Spring Security)란
- 스프링 기반의 애플리케이션의 인증, 인가 등을 담당하는 스프링 하위 프레임워크
- 인증과 권한에 대한 부분을 Filter 흐름에 따라 처리함
- 보안에 대해 체계적으로 다양한 옵션을 제공해주므로 개발자가 보안 관련 로직을 작성하지 않아도 됨.
스프링 시큐리티, SNS 로그인 구현하기
1. build.gradle 관련 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation('org.springframework.session:spring-session-jdbc:')
testImplementation 'org.springframework.security:spring-security-test'
2. SecurityConfig 작성
- WebSecurityConfigurerAdapter를 상속받는 클래스
- @EnableWebSecurity 어노테이션 -> SpringSecurityFilterChain 자동 포함
@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig extends org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter {
private final CustomOAuth2UserService customOAuth2UserService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.headers().frameOptions().disable()
.and()
// .authorizeRequests(
// authz->authz.antMatchers("/","/css/**","/js/**")
// .permitAll()
// .antMatchers("/musical/**").hasRole(Role.MEMBER.name())
// .anyRequest()
// .authenticated().and()
// )
.formLogin()
.loginPage("/login.html")
.defaultSuccessUrl("/index.html",true)
.and()
.logout()
.logoutSuccessUrl("/")
.and()
.oauth2Login()
.userInfoEndpoint()
.userService(customOAuth2UserService);
}
}
2. Enum 클래스 Role 작성
@Getter
@RequiredArgsConstructor
public enum Role {
//스프링 시큐리티에서는 권한 코드에 항상 ROLE_이 앞에 있어야만 한다.
GUEST("ROLE_GUEST","손님"),
MEMBER("ROLE_MEMBER","회원");
private final String key;
private final String title;
}
3. 네이버 로그인 API 등록
4. 카카오 로그인 API 등록
5. application.properties / application-oauth.properties 작성
6. OAuth2 관련 클래스 작성
- SessionUser
@Getter
public class SessionUser implements Serializable {
private String name;
private String email;
public SessionUser(Member member){
this.name=member.getName();
this.email=member.getEmail();
}
}
- OAuthAttributes
@Getter
public class OAuthAttributes {
private Map<String,Object> attributes;
private String nameAttributeKey;
private String name;
private String email;
@Builder
public OAuthAttributes(Map<String, Object> attributes, String nameAttributeKey, String name, String email){
this.attributes = attributes;
this.nameAttributeKey = nameAttributeKey;
this.name = name;
this.email = email;
}
//OAuth2User에서 반환하는 사용자 정보가 Map이기 때문에 값 하나하나 변환 필요.
public static OAuthAttributes of(String registrationId, String userNameAttributeName, Map<String, Object> attributes){
if("naver".equals(registrationId)){
return ofNaver("id",attributes);
}
return ofKakao(userNameAttributeName, attributes);
}
public static OAuthAttributes ofKakao(String userNameAttributeName, Map<String, Object> attributes){
Map<String, Object> response = (Map<String, Object>) attributes.get("kakao_account");
return OAuthAttributes.builder()
.name((String)attributes.get("nickname"))
.email((String)attributes.get("email"))
.attributes(attributes)
.nameAttributeKey(userNameAttributeName)
.build();
}
public static OAuthAttributes ofNaver(String userNameAttributeName, Map<String, Object> attributes){
Map<String, Object> response = (Map<String, Object>) attributes.get("response");
return OAuthAttributes.builder()
.name((String)attributes.get("name"))
.email((String)attributes.get("email"))
.attributes(response)
.nameAttributeKey(userNameAttributeName)
.build();
}
public Member toEntity() {
return Member.builder()
.name(name).email(email).role(Role.GUEST) //가입 시 기본 권한을 GUEST로 주겠다.
.build();
}
}
- CustomOAuth2UserService
@RequiredArgsConstructor
@Service
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest,OAuth2User> {
private final MemberRepository memberRepository;
private final HttpSession httpSession;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2UserService delegate = new DefaultOAuth2UserService();
OAuth2User oAuth2User = delegate.loadUser(userRequest);
//현재 로그인 진행 W중인 서비스를 구분하는 코드
String registrationId = userRequest.getClientRegistration().getRegistrationId();
//PK와 같은 의미
String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();
OAuthAttributes attributes = OAuthAttributes.of(registrationId, userNameAttributeName, oAuth2User.getAttributes());
Member member = saveOrUpdate(attributes);
httpSession.setAttribute("member", new SessionUser(member));
return new DefaultOAuth2User(
Collections.singleton(new SimpleGrantedAuthority(member.getRoleKey())),
attributes.getAttributes(),
attributes.getNameAttributeKey());
}
private Member saveOrUpdate(OAuthAttributes attributes) {
Member member = memberRepository.findByEmail(attributes.getEmail())
.orElse(attributes.toEntity());
return memberRepository.save(member);
}
}
7. 회원 관련 MemberController / MemberRepository / Member (도메인) 작성
- Member
@Getter
@NoArgsConstructor
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long mberId;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String email;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Role role;
@Builder
public Member(Long mberId, String name, String email, Role role){
this.mberId = mberId;
this.name = name;
this.email = email;
this.role = role;
}
public String getRoleKey(){
return this.role.getKey();
}
}
- MemberController
@Controller
@RequiredArgsConstructor
@Log4j2
@RequestMapping("/member")
public class MemberController {
@GetMapping("login")
public String loginMember(){
return "member/login";
}
}
- MemberRepository
import com.jungahzzzang.musicalcommunity.member.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface MemberRepository extends JpaRepository<Member,Long> {
Optional<Member> findByEmail(String email);
}
8. 로그인 화면 login.html 작성
9. 네이버 로그인 & 카카오 로그인 버튼은 각 개발자 센터에서 다운받아 사용할 수 있다.
- 네이버 개발자 센터
https://developers.naver.com/docs/login/bi/bi.md
- 카카오 개발자 센터
https://developers.kakao.com/docs/latest/ko/reference/design-guide
10. 구현 완료
반응형
'💻 my code archive > 🌈Project' 카테고리의 다른 글
[Python 프로젝트] 주제 선정, 개발 환경 구축, 프로젝트 구조 잡기 (0) | 2022.11.08 |
---|---|
[RN 프로젝트] #1 개발 환경 구축, 프로젝트 디렉토리 구조 잡기 (0) | 2022.10.05 |
[개인 프로젝트] OpenAPI 조회해온 뮤지컬 정보 화면 목록 뿌리기+Bootswatch 부트스트랩 (0) | 2022.06.24 |
[개인 프로젝트] KOPIS 공연 Open API 가져오기, XML JSON 파싱, DB 저장, 스프링 배치(Spring Batch) (4) | 2022.06.24 |
[개인 프로젝트] 뮤지컬 커뮤니티 만들기 - 준비 (0) | 2022.06.23 |