2019. 10. 10. 23:11ㆍProject B (SPMS)/Project B 파트6
인증 및 권한을 위한 테이블 설계
회원 테이블 생성
create table TBL_MEMBER(
USERID varchar2(50) not null primary key,
USERPW varchar2(100) not null,
USERNAME varchar2(100) not null,
REGDATE date default SYSDATE,
UPDATEDATE date default SYSDATE,
ENABLED char(1) default '1'
);
권한 테이블 생성
create table TBL_MEMBER_AUTH (
USERID varchar2(50) not null,
AUTH varchar2(50) not null,
constraint FK_MEMBER_AUTH foreign key(USERID) references TBL_MEMBER(USERID)
);
BCryptPasswordEncoder 클래스를 이용한 패스워드 보호
스프링 시큐리티에서 제공하는 BCryptPasswordEncoder 클래스를 이용해서 패스워드를 암호화해서 처리하도록 한다.
bcrypt는 태생 자체가 패스워드를 저장하는 용도로 설계된 해시함수로 특정 문자열을 암호화하고, 체크하는 쪽에서는 암호화된 패스워드가 가능한 패스워드인지만 확인하고 다시 원문으로 되돌리지는 못한다.
src/main/java
com.spms.config
SecurityConfig.java
package com.spms.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import com.spms.security.CustomLoginSuccessHandler;
import com.spms.security.CustomUserDetailsService;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@Configuration
@EnableWebSecurity
@Log4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Setter(onMethod_ = { @Autowired })
private DataSource dataSource;
@Bean
public UserDetailsService customUserService() {
return new CustomUserDetailsService();
}
// in custom userdetails
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserService()).passwordEncoder(passwordEncoder());
}
@Bean
public AuthenticationSuccessHandler loginSuccessHandler() {
return new CustomLoginSuccessHandler();
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/sample/all").permitAll()
.antMatchers("/sample/admin").access("hasRole('ROLE_ADMIN')")
.antMatchers("/sample/member").access("hasRole('ROLE_MEMBER')");
http.formLogin()
.loginPage("/customLogin")
.loginProcessingUrl("/login");
http.logout()
.logoutUrl("/customLogout")
.invalidateHttpSession(true)
.deleteCookies("remember-me","JSESSION_ID");
http.rememberMe()
.key("spms")
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(604800);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl repo = new JdbcTokenRepositoryImpl();
repo.setDataSource(dataSource);
return repo;
}
}
인코딩된 패스워드를 갖는 사용자 추가
생성된 사용자에 권한 추가
src/test/java
com.spms.security
MemberTests.java
[실행 순서 주의 !]
1. testInsertMember 메소드를 먼저 JUnit 실시할 것 : (@Test)
2. TBL_MEMBER 테이블에 회원이 다 들어갔는지 확인 후
3. testInsertAuth 메소드를 JUnit 실시할 것 : (@Test)
4. TBL_MEMBER_AUTH 테이블에 회원에 대한 권한이 다 들어갔는지 확인
package com.spms.security;
import java.sql.Connection;
import java.sql.PreparedStatement;
import javax.sql.DataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.spms.config.RootConfig;
import com.spms.config.SecurityConfig;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { RootConfig.class, SecurityConfig.class })
@Log4j
public class MemberTests {
@Setter(onMethod_ = @Autowired)
private PasswordEncoder pwencoder;
@Setter(onMethod_ = @Autowired)
private DataSource ds;
@Test
public void testInsertMember() {
String sql = "insert into tbl_member(userid, userpw, username) values (?,?,?)";
for (int i = 0; i < 100; i++) {
Connection con = null;
PreparedStatement pstmt = null;
try {
con = ds.getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(2, pwencoder.encode("pw" + i));
if (i < 80) {
pstmt.setString(1, "user" + i);
pstmt.setString(3, "일반사용자" + i);
} else if (i < 90) {
pstmt.setString(1, "manager" + i);
pstmt.setString(3, "운영자" + i);
} else {
pstmt.setString(1, "admin" + i);
pstmt.setString(3, "관리자" + i);
}
pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (pstmt != null) {
try {
pstmt.close();
} catch (Exception e) {
}
}
if (con != null) {
try {
con.close();
} catch (Exception e) {
}
}
}
} // end for
}
@Test
public void testInsertAuth() {
String sql = "insert into tbl_member_auth (userid, auth) values (?,?)";
for (int i = 0; i < 100; i++) {
Connection con = null;
PreparedStatement pstmt = null;
try {
con = ds.getConnection();
pstmt = con.prepareStatement(sql);
if (i < 80) {
pstmt.setString(1, "user" + i);
pstmt.setString(2, "ROLE_USER");
} else if (i < 90) {
pstmt.setString(1, "manager" + i);
pstmt.setString(2, "ROLE_MEMBER");
} else {
pstmt.setString(1, "admin" + i);
pstmt.setString(2, "ROLE_ADMIN");
}
pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (pstmt != null) {
try {
pstmt.close();
} catch (Exception e) {
}
}
if (con != null) {
try {
con.close();
} catch (Exception e) {
}
}
}
} // end for
}
}
MemberTests.java 를 JUnit으로 실행해서 대량의 회원들을 암호화된 패스워드로 데이터베이스에 저장한다.
테이블에 데이터가 잘 들어갔는지 확인
<!> 이후에 있을 파일 생성부터 할 것. EX) 회원 도메인 설계 등등.. 순서가 잠시 꼬임 ㅎ
select a.userid, a.userpw, a.username, a.regdate, b.auth
from TBL_MEMBER a, TBL_MEMBER_AUTH b
where a.userid = b.userid;
회원 도메인 설계
src/main/java
com.spms.domain
MemberVO.java
package com.spms.domain;
import java.util.Date;
import java.util.List;
import lombok.Data;
@Data
public class MemberVO {
private String userid;
private String userpw;
private String userName;
private boolean enabled;
private Date regDate;
private Date updateDate;
private List<AuthVO> authList;
}
사용자 권한 도메인 설계
src/main/java
com.spms.domain
AuthVO.java
package com.spms.domain;
import lombok.Data;
@Data
public class AuthVO {
private String userid;
private String auth;
}
멤버 매퍼
src/main/java
com.spms.mapper
MemberMapper.java 인터페이스
package com.spms.mapper;
import com.spms.domain.MemberVO;
public interface MemberMapper {
public MemberVO read(String userid);
}
src/main/resources
com
spms
mapper
MemberMapper.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.spms.mapper.MemberMapper">
<resultMap type="com.spms.domain.MemberVO" id="memberMap">
<id property="userid" column="userid"/>
<result property="userid" column="userid"/>
<result property="userpw" column="userpw"/>
<result property="userName" column="username"/>
<result property="regDate" column="regdate"/>
<result property="updateDate" column="updatedate"/>
<collection property="authList" resultMap="authMap">
</collection>
</resultMap>
<resultMap type="com.spms.domain.AuthVO" id="authMap">
<result property="userid" column="userid"/>
<result property="auth" column="auth"/>
</resultMap>
<select id="read" resultMap="memberMap">
SELECT
mem.userid, userpw, username, enabled, regdate, updatedate, auth
FROM
tbl_member mem LEFT OUTER JOIN tbl_member_auth auth on mem.userid = auth.userid
WHERE mem.userid = #{userid}
</select>
</mapper>
멤버 매퍼 유닛테스트
src/test/java
com.spms.mapper
MemberMapperTests.java
package com.spms.mapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import com.spms.config.RootConfig;
import com.spms.domain.MemberVO;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes= {RootConfig.class})
@Log4j
public class MemberMapperTests {
@Setter(onMethod_ = @Autowired)
private MemberMapper mapper;
@Test
public void testRead() {
MemberVO vo = mapper.read("admin90");
log.info(vo);
vo.getAuthList().forEach(authVO -> log.info(authVO));
}
}
멤버 매퍼 유닛 테스트 결과
|--------|-------------------------------------------------------------|---------|---------|----------------------|----------------------|-----------|
|userid |userpw |username |enabled |regdate |updatedate |auth |
|--------|-------------------------------------------------------------|---------|---------|----------------------|----------------------|-----------|
|admin90 |$2a$10$xiSjJ6uuoNcZTtCG8QMS2.WhC.xBdrserZi6mfnPMoUurpBsYjtO. |관리자90 |[unread] |2019-10-10 22:09:52.0 |2019-10-10 22:09:52.0 |ROLE_ADMIN |
|--------|-------------------------------------------------------------|---------|---------|----------------------|----------------------|-----------|
'Project B (SPMS) > Project B 파트6' 카테고리의 다른 글
[B -2-53] 스프링 웹 시큐리티를 이용한 로그인 처리 3 (0) | 2019.10.10 |
---|---|
[B -2-51] 스프링 웹 시큐리티를 이용한 로그인 처리 1 (0) | 2019.10.09 |