스프링 기초_Security_4

bitna's study note·2022년 7월 23일

스프링

목록 보기
28/54

7월 23일

1.JDBC를 이용하는 간편 인증/권한 처리
스프링 시큐리티에서는 사용자를 확인하는 '인증' 과 권한 등을 부여하는 '인가과정'으로 나누어 볼수 있다.
인증과 권한에 대한 처리는 크게 보면 Authentication Manager를 통해서 이루어지는데 이때 인증이나 권한 정보를 제공하는 존재(Provider)가 필요하고,
다시 이를 위해서 UserDetailsService라는 인터페이스를 구현한 존재를 활용하게 됨

UserDetailsService는 스프링 시큐리티 API내에 이미 여러 구현클래스가 존재하고 이걸 활용하여 인증과 인가를 한다.
일단 기존에 데이터베이스가 존재하는 상황에서 MyBatis나 기타 프레임워크 없이 사용하는 방법을 익혀 본다!!!

(1)기존테이블을 이용하는 경우
JDBC를 이용해서 인증/권한을 체크하는 방식은 크게 1)지정된 형식으로 테이블을 만드는 방법과 2)기존에 작성된 데이터베이스를 이용하는 방식이 있음.

그중 2)기존에 작성된 데이터베이스를 이용하는 방식에 대해서 알아볼 예정임

JDBC를 이용하고, 기존에 테이블이 있다면 약간의 지정된 결과를 반환하는 쿼리를 작성해 주는 작업으로도 처리가 가능함.
security:jdbc-user-service 태그에는 users-by-username-query속성과 authorities-by-username-query 속성에 적당한 쿼리문을 지정해 주면 JDBC를 이용하는 설정을 그대로 사용할 수 있다.

(2)인증/권한을 위한 테이블 설계

create table t_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 t_member_auth(
userid VARCHAR2(50) not null,
auth VARCHAR2(50) not null,
constraint fk_member_auth foreign key(userid) references t_member(userid)
);

(3)BCryptPasswordEncoder 클래스를 이용한 패스워드 보호
스프링 시큐리티에서 제공하는 BCryptPasswordEncoder 클래스를 이용해서 패스워드를 암호화 해서 처리하도록 함.
BCrypt는 패스워드를 저장하는 용도로 설계된 해시함수로 특정 문자열을 암호화하고, 체크하는 쪽에서는 암호화된 패스워드가 가능한 패스워드인지만 확인하고 다시 원문으로 되돌리지는 못합니다.

(4)security-context.xml 수정

<?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:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security 
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- 빈으로 등록 하기 -->
<bean id="customAccessDenied" class="com.keduit.security.CustomAccessDeniedHandler"></bean>
<bean id="customLoginSuccess" class="com.keduit.security.CustomLoginSuccessHandler"></bean>
<bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>


<security:http>

		<security:intercept-url pattern="/sample/all" access="permitAll"/>
		<security:intercept-url pattern="/sample/member" access="hasRole('ROLE_MEMBER')"/>
		<security:intercept-url pattern="/sample/admin" access="hasRole('ROLE_ADMIN')"/>
	
	<!--/accessError 라는 uri로 접근제한 시 보이는 화면을 처리합니다-->
	<security:access-denied-handler error-page="/customAccessDenied"/>
	
	<!-- 로그인페이지 따로 URI 설정하기 -->
	<security:form-login login-page="/customLogin" authentication-success-handler-ref="customLoginSuccess"/>
	
	<!-- 로그아웃 페이지 URI 설정하기 -->
	<security:logout logout-url="/customLogout" invalidate-session="true"/>
	
</security:http>

<security:authentication-manager>
	<security:authentication-provider>
		<security:jdbc-user-service data-source-ref="dataSource" 
		users-by-username-query="select userid, userpw, enabled from t_member where userid=?"
		authorities-by-username-query="select userid, auth from t_member_auth where userid=?"/>
		<security:password-encoder ref="bcryptPasswordEncoder"/>
	</security:authentication-provider>
</security:authentication-manager>

</beans>

(5)test 코드 작성하기
실제 데이터베이스에 기록하는 회원 정보는 BcryptPasswordEncoder를 이용해서 암호화된 상태로 넣어주어야 하므로 테스트 코드를 작성해서 처리

package com.keduit.security;

@RunWith(SpringJUnit4ClassRunner.class)//이 클래스로 잘돌아가는지 test해볼꺼야
@ContextConfiguration({"file:src/main/webapp/WEB-INF/spring/root-context.xml",
						"file:src/main/webapp/WEB-INF/spring/security-context.xml"})//참고할 경로는 여기야
@Log4j //화면에 뿌려주는건 Log4j가 관리 할거야 
public class MemberTest {
	
	@Setter(onMethod_ = @Autowired)
	private PasswordEncoder pwencoder;
	
	@Setter(onMethod_ = @Autowired)
	private DataSource ds;
	
	@Test
	public void testInsertMember() {
		String sql="insert into t_member(userid,userpw,username)values(?,?,?)";
		
		for(int i=0;i<100;i++) {
			Connection conn=null;
			PreparedStatement pstmt=null;
			
			try {
				conn=ds.getConnection();
				pstmt=conn.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) {
						e.printStackTrace();
					}
				}
				
				if(conn !=null) {
					try {
						conn.close();
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
				
			}//	end finally
			
		} //end for
		
	}//end testInsertMember

}

위에 테스트 코드로 사용자생성이 완료 되었다면 t_member_auth 테이블애 사용자의 권한에 대한 정보도 추가해 줘야함.

package com.keduit.security;

@RunWith(SpringJUnit4ClassRunner.class)//이 클래스로 잘돌아가는지 test해볼꺼야
@ContextConfiguration({"file:src/main/webapp/WEB-INF/spring/root-context.xml",
						"file:src/main/webapp/WEB-INF/spring/security-context.xml"})//참고할 경로는 여기야
@Log4j //화면에 뿌려주는건 Log4j가 관리 할거야 
public class MemberTest {
	
	@Setter(onMethod_ = @Autowired)
	private PasswordEncoder pwencoder;
	
	@Setter(onMethod_ = @Autowired)
	private DataSource ds;
	
	@Test
	public void testInsertAuth() {
		String sql="insert into t_member_auth(userid,auth) values (?,?)";
		
		for(int i=0; i<100; i++) {
			Connection conn=null;
			PreparedStatement pstmt=null;
			
			try {
				conn=ds.getConnection();
				pstmt=conn.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) {
						e.printStackTrace();
					}
				}
				
				if(conn !=null) {
					try {
						conn.close();
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
				
			}//	end finally			
		}//end for
	}//end testInsertAuth 
}

(6)쿼리로 이용해 인증
위와 같이 테이블 구조를 이용하는 경우에는 인증을 하는데 필요한 쿼리(users-by-username-query) 권한을 확인하는데 필요한 쿼리(authorities-by-username-query) 를 이용해 처리함
위에 수정된 security-context.xml 부분에서 인증 쿼리부분은 아래 부분임

<security:authentication-manager>
	<security:authentication-provider>
		<security:jdbc-user-service data-source-ref="dataSource" 
		users-by-username-query="select userid, userpw, enabled from t_member where userid=?"
		authorities-by-username-query="select userid, auth from t_member_auth where userid=?"/>
		<security:password-encoder ref="bcryptPasswordEncoder"/>
	</security:authentication-provider>
</security:authentication-manager>

</beans>
profile
좋은개발자가 되기위한 삽질기록 노트

0개의 댓글