인증 화면에서 입력받은 데이터가 DB에 저장된 데이터인지 검증하는  작업을 해보았습니다. 이 과정에서 간편하게 각 사용자별 권한을 컬럼으로 추가하고 작업을 해 보았는데요. 사실 권한에 대한 관리는 간단하지 않습니다. 그룹별로  권한을 부여하고 관리하거나 사용자별로 관리할 수 있는데 spring.io사이트의 "Appendix" 부분에 보면 40 chapter에 "Security Database Schema"부분에 정리가 되어 있습니다. 

 

우리는 그룹별로 권한을 관리해 보도록 하겠습니다.  그룹과 권한을 별도의 테이블로 관리하고 그룹별 권한을 관리하여 사용자가 어떤 권한을 갖고 있을지를 db로 관리해보겠습니다. 실제 작업을 하는 경우 그룹별로 접속할 수 있는 리소스나 접근할 수 있는 메서드 등도 db로 관리해야 합니다.

 

먼저 simple테스트를 위해 앞에서 추가했던 member테이블의 authority컬럼을 삭제하고 이에 따라 추가했던 DTO의 컬럼, 생성자, setter/getter메서드를 제거하도록 하겠습니다.

alter table member
drop column authority;

 

먼저 그룹을 정리한 그룹 테이블이 필요합니다. 우리 시스템에 미리 등록해 놓은 job테이블이 job을 그룹별로 나누어 정의해 놓은 테이블이므로 이 테이블은 group 테이블로 사용하겠습니다.

#그룹테이블
create table job(
	job_id varchar2(10) primary key,
	job_name varchar2(15),
	job_category varchar2(15),
	menupath varchar2(50));

#데이터
insert into job values('j001','영업','영업관리','/menu/sales_menu.jsp');
insert into job values('j002','인사','인사관리','/menu/insa_menu.jsp');
insert into job values('j003','전산','전산관리','/menu/it_menu.jsp');
insert into job values('j004','경영지원','경영관리','/menu/manage_menu.jsp');
insert into job values('j005','재무','재무관리','/menu/jaemu_menu.jsp');

 

권한 테이블은 다음과 같습니다. authorityid는 권한 아이디,  authorityname은 권한 명입니다. systemadmin, admin, insaadmin은 관리자와 관련된 권한명이고 insauser, user는 일반 인증된 사용자의 권한명입니다.

#권한테이블
create table authority(
     authorityid varchar2(20) primary key,
     authorityname varchar2(20));

#데이터
insert into authority values('auth01','systemadmin');
insert into authority values('auth02','admin');
insert into authority values('auth03','insaadmin');
insert into authority values('auth04','insauser');
insert into authority values('auth05','user');

 

그룹과 권한에 대한 정의를 하기 위해 테이블을 정의했다면 각 그룹별로 각각의 그룹이 어떤 권한을 갖고 있는지 정의하는 테이블이 있어야 합니다. 이렇게 그룹별 권한을 정의하다 보면 하나의 그룹이 여러 개의 권한을 갖고 있는 상황이 발생하기도 합니다. 예를 들어 j003그룹 같은 경우 auth01, auth02, auth05의 권한을 갖고 있습니다. 

#그룹별권한테이블
drop table groupauthority;
create table groupauthority(
     groupid varchar2(20),
     authorityid varchar2(20));


#데이터
insert into groupauthority values('j001','auth05');
insert into groupauthority values('j002','auth04');
insert into groupauthority values('j002','auth05'); 
insert into groupauthority values('j003','auth05'); 
insert into groupauthority values('j003','auth01'); 
insert into groupauthority values('j003','auth02'); 
insert into groupauthority values('j004','auth03'); 
insert into groupauthority values('j004','auth05'); 
insert into groupauthority values('j005','auth03'); 
insert into groupauthority values('j005','auth05'); 

 

member테이블에 저장된 직원의 정보에 dept테이블에 대한 정보가 저장되어 있고 dept에 job에 대한 정보가 저장되어 있으므로 각 사원의 권한 정보는 조인을 하여 구하면 됩니다.

dept테이블의 정보는 다음과 같습니다.

#부서 테이블
create table dept(
	deptno varchar2(15) primary key,
	deptname varchar2(15),
	deptStartDay date,
	deptEndDay date,
	deptlevel varchar2(5),
	deptstep varchar2(5),
	deptuppercode varchar2(15),
	job_category varchar2(15),
	mgr_id varchar2(15),
	deptaddr varchar2(50),
	depttel varchar2(15)
);

#데이터
insert into dept values('d001','인사팀','1999/9/27',null,'2','1','dg001','j002','9401023jang','서울시 서초구 kitri빌딩7층','02-225-1111');
insert into dept values('d002','재무팀','1999/9/27',null,'2','1','dg001','j005','9401023jang','서울시 서초구 kitri빌딩7층','02-225-1122');
insert into dept values('d003','개발팀','1999/9/27',null,'2','1','dg003','j003','9401023jang','서울시 서초구 kitri빌딩6층','02-225-1133');
insert into dept values('d0031','디자인팀','1999/9/27',null,'3','1','d003','j003','9401023jang','서울시 서초구 kitri빌딩6층','02-225-1121');
insert into dept values('d0032','Ajax팀','1999/9/27',null,'3','2','d003','j003','9401023jang','서울시 서초구 kitri빌딩1층','02-225-1131');
insert into dept values('d004','개발지원','1999/9/27',null,'2','1','dg001','j003','9401023jang','서울시 서초구 kitri빌딩1층','02-225-1144');
insert into dept values('d005','시스템지원팀','1999/9/27',null,'2','1','dg001','j003','9401023jang','서울시 서초구 kitri빌딩2층','02-225-1155');
insert into dept values('d006','총무과','1999/9/27',null,'2','1','dg001','j004','9401023jang','서울시 서초구 kitri빌딩3층','02-225-1166');
insert into dept values('dg001','경영지원본부','1999/9/27',null,'1','1',null,'j004','9401023jang','서울시 서초구 kitri빌딩4층','02-225-1177');
insert into dept values('dg002','영업본부','1999/9/27',null,'1','1',null,'j001','9401023jang','서울시 서초구 kitri빌딩5층','02-225-1188');
insert into dept values('dg003','IT지원센터','1999/9/27',null,'1','1',null,'j003','9401023jang','서울시 서초구 kitri빌딩6층','02-225-1199');
insert into dept values('d007','교육부','1999/9/27',null,'2','1','dg001','j004','9401023jang','서울시 서초구 kitri빌딩6층','02-225-1100');
insert into dept values('d008','기획실','1999/9/27',null,'2','1','dg001','j004','9401023jang','서울시 서초구 kitri빌딩5층','02-225-1102');
insert into dept values('d009','영업1팀','1999/9/27',null,'2','1','dg001','j001','9401023jang','서울시 서초구 kitri빌딩8층','02-225-1222');
insert into dept values('d010','기업영업본부','1999/9/27',null,'2','1','dg001','j001','9401023jang','서울시 서초구 kitri빌딩9층','02-225-13331');
insert into dept values('d011','영업2팀','1999/9/27',null,'2','1','dg001','j001','9401023jang','서울시 서초구 kitri빌딩10층','02-225-1444');
insert into dept values('d012','마케팅실','1999/9/27',null,'2','1','dg001','j004','9401023jang','서울시 서초구 kitri빌딩10층','02-225-1555');

 

직원정보는 다음과 같습니다.

insert into member values('9401023jang','1234','장동건','731111-1111111','1973/11/11','1','0','부장','팀장','3급20호봉','1999/10/01',null,'d001','재직','222-222','서울시 봉천구','좋은동네','02-858-1111','02-858-1111','010-111-2222','jang@naver.com','jang.jpg');
insert into member values('0111022kim','1234','김범룡','800521-1111111','1980/09/01','1','0','과장','팀장','4급20호봉','2007/02/27',null,'d002','재직','222-222','서울시 봉천구','좋은동네','02-858-1111','02-858-1111','010-111-3333','kbr@naver.com','kbr.jpg');
insert into member values('92115kim','1234','김서연','901012-1111111','1990/11/11','1','1','과장','팀장','4급20호봉','2010/10/01',null,'d003','재직','222-222','서울시 봉천구','좋은동네','02-858-1111','02-858-1111','010-111-4444','jang@naver.com','kim.jpg');
insert into member values('881011kim','1234','김동현','721012-1111111','1990/11/11','1','1','과장','팀장','4급20호봉','2010/10/01',null,'d006','재직','222-222','서울시 봉천구','좋은동네','02-858-1111','02-858-1111','010-111-4444','jang@naver.com','kimdong.jpg');
insert into member values('787871lee','1234','이민호','721012-1111111','1990/11/11','1','1','대리','사원','4급20호봉','2010/10/01',null,'dg001','재직','222-222','서울시 봉천구','좋은동네','02-858-1111','02-858-1111','010-111-4444','jang@naver.com','leemin.jpg');
insert into member values('17171park','1234','박문수','721012-1111111','1990/11/11','1','1','대리','사원','4급20호봉','2010/10/01',null,'d0032','재직','222-222','서울시 봉천구','좋은동네','02-858-1111','02-858-1111','010-111-4444','jang@naver.com','park.jpg');
insert into member values('9871kim','1234','김어준','721012-1111111','1990/11/11','1','1','과장','팀장','4급20호봉','2010/10/01',null,'d009','재직','222-222','서울시 봉천구','좋은동네','02-858-1111','02-858-1111','010-111-4444','jang@naver.com','jjangkim.jpg');
insert into member values('lee0idjj','1234','안정환','721012-1111111','1990/11/11','1','1','차장','팀장','4급20호봉','2010/10/01',null,'dg002','재직','222-222','서울시 봉천구','좋은동네','02-858-1111','02-858-1111','010-111-4444','jang@naver.com','ansoccer.jpg');
insert into member values('djdiwjs','1234','차범근','721012-1111111','1990/11/11','1','1','사원','사원','4급20호봉','2010/10/01',null,'dg003','재직','222-222','서울시 봉천구','좋은동네','02-858-1111','02-858-1111','010-111-4444','jang@naver.com','chasoccer.jpg');
insert into member values('282djdid','1234','박지성','721012-1111111','1990/11/11','1','1','대리','파트장','4급20호봉','2010/10/01',null,'d001','재직','222-222','서울시 봉천구','좋은동네','02-858-1111','02-858-1111','010-111-4444','jang@naver.com','parksoccer.jpg');
insert into member values('Yeona1231','1234','김연아','721012-1111111','1990/11/11','1','1','대리','파트장','4급20호봉','2010/10/01',null,'d003','재직','222-222','서울시 봉천구','좋은동네','02-858-1111','02-858-1111','010-111-4444','jang@naver.com','parksoccer.jpg');

 

예를 들어 "9401023jang" 사원번호를 갖고 있는 직원 같은 경우 "d001"부서에 근무하며 "d001"부서는 "j002"그룹입니다.

"j002"그룹은 "auth04", "auth05"권한을 갖고 있으므로 "9401023jang"사원의 권한은 insauser와 user권한을 갖고 있습니다.

 

 

사실 같은 부서 안에서도 직급이 어떻게 되는지에 따라 다르게 권한을 정의해야 합니다. 복잡한 작업입니다. 그러나 이렇게 테이블로 권한을 관리하는 작업을 연습해 보면 좀 더 복잡한 작업도 얼마든지 할 수 있을 것입니다.

 

 

MemberAuthoritysDTO

이제 권한 정보를 db에서 조회한 member정보를 담고 있는 DTO에서 추출하는 것이 아니라 별도의 메서드를 정의하여 여러 테이블을 조인해서 작업해야 합니다.

우선 사원별 권한명이 필요하므로 이 정보를 모델링한 DTO클래스를 ktds.erp.emp.authentication 패키지에 작성합니다.

 

package ktds.erp.emp.authentication;

public class MemberAuthoritysDTO {
	private String id;
	private String authorityname;
	public MemberAuthoritysDTO() {
		
	}
	public MemberAuthoritysDTO(String id, String authorityname) {
		super();
		this.id = id;
		this.authorityname = authorityname;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	
	public String getAuthorityname() {
		return authorityname;
	}
	public void setAuthorityname(String authorityname) {
		this.authorityname = authorityname;
	}
	@Override
	public String toString() {
		return "MemberAuthoritysDTO [id=" + id + ", authorityid=" + authorityname + "]";
	}
}

 

mybatis-cofig.xml

위에서 작성한 dto를 mapper내부에서 사용하기 위해 등록합니다.

<typeAlias type="ktds.erp.emp.authentication.MemberAuthoritysDTO" alias="authority"/>

 

emp.xml

로그인을 하면 로그인한 사원의 모든 권한을 조회해서 리턴할 수 있도록 emp.xml mapper에 <select> 엘리먼트에 추가합니다. 조회할 정보가 사원별 권한명이므로 member, dept,  job, groupauthority, authority테이블에서 조인해야 합니다.

<select id="authorityList" parameterType="String" resultType="authority">
		select m.id,a.authorityname 
		from member m, dept d, job g, groupauthority ga, authority a
		where m.deptno = d.deptno
      		and d.job_category = g.job_id
      		and ga.groupid = g.job_id
      		and a.authorityid = ga.authorityid
      		and m.id=#{id}
</select>

 

AuthorityDAO

위에서 정의 테이블들을 조인해서 사원별 권한 정보를 리턴하는 메서드를 정의합니다.

package ktds.erp.emp.authentication;

import java.util.List;

public interface AuthorityDAO {
	//사용자별권한을 리턴하는 메소드
	List<MemberAuthoritysDTO> getAuthorityList(String id);
}

 

AuthorityDAOImpl

위에서 정의한 mapper를 실행할 수 있도록 AuthorityDAOImpl을 정의합니다.

package ktds.erp.emp.authentication;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class AuthorityDAOImpl implements AuthorityDAO{
	@Autowired
	SqlSession sqlSession;

	@Override
	public List<MemberAuthoritysDTO> getAuthorityList(String id) {
		return sqlSession.selectList("ktds.erp.emp.authorityList", id);
	}
}

 

SecurityLoginService

앞에서 작성한 SecurityLoginService를 수정해서 AuthorityDAO의 메소드를 호출하여 권한을 받아올 수 있도록 수정합니다.

package ktds.erp.emp.authentication;


import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
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 ktds.erp.emp.EmpDAO;
import ktds.erp.emp.LoginDTO;

@Service("loginService")
public class SecurityLoginService implements UserDetailsService {
	@Autowired  
	EmpDAO dao;
	@Autowired 
	AuthorityDAO authDao;
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		System.out.println(username+"........loadUserByUsername");
		//로그인 사용자 db에서 조회하기
		LoginDTO user = dao.findById(username);
		System.out.println("user===>"+user);
		UserDetails loginUser = null;
		
		//권한정보 조회하기
		List<MemberAuthoritysDTO> authorityList = authDao.getAuthorityList(username);
		List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
		System.out.println(authorityList);
		for(int i=0;i<authorityList.size();i++) {
			authorities.add(new SimpleGrantedAuthority(authorityList.get(i).getAuthorityname()));
		}
		System.out.println(authorities);
		//인증정보와 권한을 User객체로 변환하여 리턴하기
		System.out.println(authorities);
		loginUser = new User(user.getId(), user.getPass() ,authorities);
		System.out.println(loginUser);
		return loginUser;
	}
}

 

이제 실행해 볼까요? 어떤 아이디로 접속하더라도 아래와 같이 response 되며 접속이 안됩니다. 

 

왜 그럴까요? DB에서 조회되고 있기 때문입니다. 우리는 db에 권한명을 admin, user로 저장했는데 설정 파일에서는 ROLE_ADMIN, ROLE_USER로 등록해서 작업하고 있죠?

설정 파일의 권한명을 모두 아래와 같이 db에 저장된 권한명으로 바꿔줍니다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:security="http://www.springframework.org/schema/security"
	xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<security:http pattern="/**/*.js" security="none" />
	<security:http pattern="/**/*.css" security="none" />
	<security:http pattern="/images/**" security="none" />
	<security:http auto-config="true" use-expressions="true">
		<!-- <security:intercept-url pattern="/images/**" access="ROLE_ANONYMOUS"/> -->
		<security:intercept-url pattern="/admin/**"	access="hasRole('admin')" />
		<security:intercept-url pattern="/emp/login" access="permitAll" />
		<security:intercept-url pattern="/emp/logout.do" access="hasAnyRole('user','admin')" />
		<security:intercept-url pattern="/index.do" access="permitAll" />
		<security:intercept-url pattern="/**/user/**" access="hasAnyRole('user','admin')" />
		<security:intercept-url pattern="/board/*"	access="permitAll" />
		<security:intercept-url pattern="/emp/*" access="hasRole('admin')" />
		<security:intercept-url pattern="/**" access="hasAnyRole('user','admin')" />
		<security:form-login username-parameter="id"
			password-parameter="pass" login-page="/emp/login" default-target-url="/index.do"
			authentication-failure-url="/emp/login.do?fail=true" />
		<security:logout delete-cookies="true"
			logout-success-url="/emp/login" logout-url="/emp/logout.do"
			invalidate-session="true" />
	</security:http>
	<security:authentication-manager>
		<security:authentication-provider 	user-service-ref="loginService">
		</security:authentication-provider>
	</security:authentication-manager>
	<import resource="spring-config.xml" />  
</beans>

web.xml을 수정하고 다시 실행하면 잘 실행됩니다.

+ Recent posts