React - 11(Spring Security - 2)

월요일좋아·2023년 1월 20일
1

React

목록 보기
9/11

DB 연결

1. appication.properties - 데이터 베이스 접속 설정

# 데이터 베이스 접속 설정
server.port=8080
spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.hikari.jdbc-url=jdbc:mysql://58.239.58.243:3306/java505_team5_final
spring.datasource.hikari.username=java505_team5
spring.datasource.hikari.password=java505_team5_1234
spring.datasource.hikari.connection-test-query=SELECT 1

mybatis.configuration.map-underscore-to-camel-case=true

2. build.gradle 에서 mybatis, mySQL Driver 주석 풀기 - 재로드

3. DatabaseConfiguration.java 생성

  • DatabaseConfiguration.java
package com.bitc.securitytest.configuration;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import lombok.RequiredArgsConstructor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;

@Configuration
@RequiredArgsConstructor
@PropertySource("classpath:/application.properties")
public class DatabaseConfiguration {

  private final ApplicationContext appContext;

  @Bean
  @ConfigurationProperties(prefix = "spring.datasource.hikari")
  public HikariConfig hikariConfig() {
    return new HikariConfig();
  }

  @Bean
  public DataSource dataSource() throws Exception {
    DataSource dataSource = new HikariDataSource(hikariConfig());
    System.out.println(dataSource.toString());
    return dataSource;
  }

  @Bean
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
    ssfb.setDataSource(dataSource);
    ssfb.setMapperLocations(appContext.getResources("classpath:/sql/**/sql-*.xml"));
    ssfb.setConfiguration(mybatisConfig());

    return ssfb.getObject();
  }

  @Bean
  public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory ssf) {
    return new SqlSessionTemplate(ssf);
  }

  @Bean
  @ConfigurationProperties(prefix = "mybatis.configuration")
  public org.apache.ibatis.session.Configuration mybatisConfig() {
    return new org.apache.ibatis.session.Configuration();
  }
}



dto 패키지 생성 - dto 파일 생성

  • ClubMemberDto.java
package com.bitc.securitytest.dto;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.HashSet;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class ClubMemberDto {
  private String email;
  private String password;
  private String name;
  private boolean fromSocial;
  private Set<ClubMemberRole> roleSet = new HashSet<>();

  public void addMemberRole(ClubMemberRole clubMemberRole) {
    roleSet.add(clubMemberRole);
  }
}
  • dto/ClubAuthMemberDto
    User 상속받아서 사용
@Setter
@Getter
@ToString
public class ClubAuthMemberDto extends User{
  
}

package com.bitc.securitytest.dto;

import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;

import java.util.Collection;

@Setter
@Getter
@ToString
public class ClubAuthMemberDto extends User {
  private String email;
  private String name;
  private boolean fromSocial;

  public ClubAuthMemberDto(String username, String password, boolean fromSocial, Collection<? extends GrantedAuthority> auth) {
    super(username, password, auth);
    this.email = username;
    this.fromSocial = fromSocial;
  }

}
  • dto/ClubMemberRole.java
    class ====> enum 으로 수정!!
package com.bitc.securitytest.dto;

// class -> enum 
public enum ClubMemberRole {
  ADMIN, USER, MANAGER
}

mapper/ClubMEmberMapper.java 생성 (인터페이스)

package com.bitc.securitytest.mapper;

import com.bitc.securitytest.dto.ClubMemberDto;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.Optional;

@Mapper
public interface ClubMEmberMapper {
  public Optional<ClubMemberDto> findByEmail(@Param("email") String email, @Param("social") boolean social);
}

service/ClubUserDetailsService.java 생성
implements UserDetailsService

  • private final ClubMEmberMapper clubMEmberMapper;
    alt+Enter -> 메서드 구현
package com.bitc.securitytest.service;

import com.bitc.securitytest.dto.ClubMemberDto;
import com.bitc.securitytest.dto.ClubMemberRole;
import com.bitc.securitytest.mapper.ClubMEmberMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Optional;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class ClubUserDetailsService implements UserDetailsService {

  private final ClubMEmberMapper clubMEmberMapper;


  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

    Optional<ClubMemberDto> result = clubMEmberMapper.findByEmail(username, false);

    if (result.isEmpty()) {
      throw new UsernameNotFoundException("이메일 및 비밀번호를 확인하세요.");
    }
    ClubMemberDto member = result.get();
    member.addMemberRole(ClubMemberRole.USER);

    ClubAuthMemberDto clubAuthMemberDto = new ClubAuthMemberDto(
        member.getEmail(),
        member.getPassword(),
        member.isFromSocial(),
        member.getRoleSet().stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role.name())).collect(Collectors.toSet())
    );

    clubAuthMember.setName(member.getName());
    clubAuthMember.setFromSocial(member.isFromSocial());

    
    return clubAuthMember;
  }
}

resources/sql 경로 생성

  • sql/sql-member.xml 생성
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.bitc.securitytest.mapper.ClubMEmberMapper">
    <select id="findByEmail" parameterType="map" resultType="com.bitc.securitytest.dto.ClubMemberDto">
        SELECT *
        FROM club_member
        WHERE email = #{email}
          AND from_social = #{social}
    </select>
</mapper>

주석 추가

ClubAuthMemberDto.java

package com.bitc.securitytest.dto;

import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;

import java.util.Collection;


// User 클래스를 [상속]받아 인증과 관련된 정보르 저장하는 DTO
// User 클래스 : 스프링 시큐리티에서 관리하는 사용자 정보 클래스
@Setter
@Getter
@ToString
public class ClubAuthMemberDto extends User {

  // User 클래스에서 제공하는 username, password, authorities 사용해야 함
  // username : 스프링 시큐리티에서 사용하는 사용자 ID
  // password : 스프링 시큐리티에서 사용하는 사용자 PW
  // authorities : 스프링 시큐리티에서 사용하는 사용자 권한

  private String email; // 사용자 id 로 사용되는 변수
  private String name; // 사용자 이름
  private boolean fromSocial;

  public ClubAuthMemberDto(String username, String password, boolean fromSocial, Collection<? extends GrantedAuthority> auth) {

    // 객체 생성 시 매개변수로 받은 사용자 id와 비밀번호, 인증 권한을 부모 클래스의 생성자를 실행하여 데이터를 저장함
    super(username, password, auth);  // 부모인 User 에 데이터 넘기기 -> 자식 클래스에서 사용가능

    // User 클래스에서 사용자 id는 username 변수이므로 해당 변수를 사용자 id 역할을 하는 email 변수에 저장
    this.email = username;
    this.fromSocial = fromSocial;
  }
}
  • ClubMemberDto.java
package com.bitc.securitytest.dto;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.HashSet;
import java.util.Set;


// 데이터 베이스의 정보를 가져오기 위한 클래스

@AllArgsConstructor
@NoArgsConstructor
@Data
public class ClubMemberDto {
  private String email; // 해당 DB 에서 id 의 역할을 하는 컬럼
  private String password;
  private String name;
  private boolean fromSocial;


  // 사용자의 권한을 저장하는 변수
  // 만약 DB에 컬럼이 따로 존재 시, 일반 String 타입으로 사용해도 됨
  private Set<ClubMemberRole> roleSet = new HashSet<>();

  
  // 사용자 권한을 추가하기 위한 메서드일뿐
  // 만약 데이터 베이스에 컬럼이 따로 존재 시, 해당 메서드는 필요 없음
  public void addMemberRole(ClubMemberRole clubMemberRole) {
    roleSet.add(clubMemberRole);
  }
}
  • ClubMemberRole.jvav
package com.bitc.securitytest.dto;

// 사용자의 권한을 지정해 놓은 것만 사용하도록 enum 타입으로 설정
public enum ClubMemberRole {
  ADMIN, USER, MANAGER
}
  • ClubUserDetailsService.java
package com.bitc.securitytest.service;

import com.bitc.securitytest.dto.ClubAuthMemberDto;
import com.bitc.securitytest.dto.ClubMemberDto;
import com.bitc.securitytest.dto.ClubMemberRole;
import com.bitc.securitytest.mapper.ClubMEmberMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Optional;
import java.util.stream.Collectors;

// UserDetailsService 사용자 정보를 데이터 베이스에서 가져올 경우 UserDetailsService 를 상속받아 loadUserByUsername() 메서드를 구현해야 함
@Service
@RequiredArgsConstructor
public class ClubUserDetailsService implements UserDetailsService {

  private final ClubMEmberMapper clubMEmberMapper;


  // DB 에서 사용자 정보를 가져올 경우 반드시 UserDetailsService 의 loadUserByUsername() 메서드를 구현해야 함
  // loadUserByUsername() 메서드는 UserDetails 인터페이스를 구현한 클래스객체를 반환해야 함
  // UserDetails 인터페이스를 상속받아 구현한 클래스의 객체를 스프링 시큐리티에서 확인하여 인증된 사용자인지 아닌지를 판단함
  // 사용자가 로그인 페이지에서 로그인 시, 스프링 시큐리티가 먼저 데이터를 받아서 loadUserByUsername() 메서드에 사용자 id 를 매개변수로 사용해서 실행
  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

    // Mybatis 의 mapper 를 이용하여 사용자 정보를 가져옴
    // Optional : DB 사용 시, 데이터 조회 후 데이터가 null 이 들어올 경우 오류가 발생할 수 있는 부분을 안전하게 사용하기 위한 데이터 타입(제네릭)
    Optional<ClubMemberDto> result = clubMEmberMapper.findByEmail(username, false);

    // DB에 해당 사용자 정보가 있는지 확인
    if (result.isEmpty()) {
      throw new UsernameNotFoundException("이메일 및 비밀번호를 확인하세요.");
    }
    // Optional 타입에 저장된 정보를 가져옴
    ClubMemberDto member = result.get();  // 전체 데이터 가져오기

    // ↓ 데이터베이스에 등급 권한 정보 컬럼이 있으면 필요없는 내용
    // (사용자 정보에 사용 등급 권한 설정)
    member.addMemberRole(ClubMemberRole.USER);

    // 로그인 인증 정보를 가지고 있는 ClubAuthMemberDto 클래스 타입의 객체 생성
    // 매개변수로 DB 에서 가져온 정보를 넘겨서 사용자가 입력한 사용자 id를 가지고 있는 로그인 인증된 객체가 생성됨
    ClubAuthMemberDto clubAuthMember = new ClubAuthMemberDto(
        member.getEmail(),
        member.getPassword(),
        member.isFromSocial(),
        // 스프링 시큐리티에서 사용하는 권한 정보는 모두 "ROLE_권한" 형태로 되어 있음
        // map(매개변수 role -> 인증정보 신규생성 (new SimpleGrantedAuthority))
        member.getRoleSet().stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role.name())).collect(Collectors.toSet())
    );

    clubAuthMember.setName(member.getName());
    clubAuthMember.setFromSocial(member.isFromSocial());


    // 로그인 인증 정보를 가지고 있는 객체를 반환 시 스프링 시큐리티가 처리해 줌
    return clubAuthMember;
  }
}

0개의 댓글