# 데이터 베이스 접속 설정
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
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();
}
}
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);
}
}
@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;
}
}
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
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 경로 생성
<?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;
}
}
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);
}
}
package com.bitc.securitytest.dto;
// 사용자의 권한을 지정해 놓은 것만 사용하도록 enum 타입으로 설정
public enum ClubMemberRole {
ADMIN, USER, MANAGER
}
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;
}
}