[Spring Framework]Spring Security 적용하기 - 4) DB 연동, MyBatis 연결
- -
이번 포스팅에서는 데이터 베이스와 연결하여 로그인을 할 수 있도록 수정하고, Mybatis와 연동한 데이터 조회하기까지 다뤄보려고 합니다.
데이터 베이스에 접속하기 위해서 DBCP를 사용할 것이고, 사용한 데이터 베이스는 오라클임을 참고해주세요.
여러 시도 결과, JDK 버전, DBCP 버전, OJDBC 버전 등등 버전의 호환성을 맞춰주는 것이 중요했습니다. 따라서 이 포스팅은 JDK 1.7 버전 기준이기 때문에 환경이 다르다면 각 개발 환경에 맞는 버전을 준비해주셔야 합니다.
1. DB 접속(JUnit 단위테스트)
1-1. dependency 추가.
먼저 DB에 접속이 되는지 확인하기 위해 Junit으로 테스트를 해본다.
(Junit이란, 자바에서 독립된 단위테스트를 지원해주는 프레임워크이다)
pom.xml 하단에 보면 <!-- Test --> 라는 주석으로 junit <dependency>를 확인할 수 있다. 다른 설정을 해주지 않았다면 기본 4.7버전으로 되어있을 텐데 버전 호환을 위해 4.12로 수정해준다.
junit 위에 추가된 spring-test는 어노테이션 사용을 통해 편리한 테스트를 지원해주는 라이브러리이다. 테스트에 사용하기 위해 추가해준다.
pom.xml
<!-- Spring DB Test : https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
DB 연결을 위한 Spring JDBC, OJDBC, DBCP2 dependency 추가
<!-- Spring JDBC 라이브러리 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- ojdbc7 : https://mvnrepository.com/artifact/com.oracle/ojdbc7 -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc7</artifactId>
<version>12.1.0.2</version>
</dependency>
<!-- DBCP2 : https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.1.1</version>
</dependency>
1-2. 테스트를 위한 OracleConnectionTest.java 생성
경로 : [test]-[java]-[com]-[company]
OracleConnectionTest.java
package com.company;
import java.sql.Connection;
import java.sql.DriverManager;
import org.junit.Test;
public class OracleConnectionTest {
private static final String DRIVER ="oracle.jdbc.driver.OracleDriver";
private static final String URL ="jdbc:oracle:thin:@접속 DB 정보";
private static final String USER ="접속 DB 아이디";
private static final String PW ="접속 DB 패스워드";
@Test
public void testConnect() throws Exception{
Class.forName(DRIVER);
try(Connection con = DriverManager.getConnection(URL, USER, PW)){
System.out.println(con);
}catch(Exception e) {
e.printStackTrace();
}
}
}
1-3. 우클릭 후 JUnit Test를 실행한다.
[성공 화면]
2. 스프링 Mybatis 연동
자바 코드에서 SQL 명령어를 분리시키기 위해 Mybatis를 사용하기로 한다.
2-1. Mybatis 연동에 필요한 dependency 추가
pom.xml
<!-- MyBatis : https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.7</version>
</dependency>
<!-- Spring MyBatis : https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.1</version>
</dependency>
2-2. root-context.xml에 연결하고자 하는 DB 정보를 입력한다.
root-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:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<!-- 데이터베이스 연결 -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@접속 DB 정보" />
<property name="username" value="접속 DB 아이디"/>
<property name="password" value="접속 DB 패스워드"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
2-2. Junit으로 간단히 테스트해보고 지나간다.
1-2와 같은 경로에 MybatisTest.java 파일을 생성한다.
MybatisTest.java
package com.company;
import java.sql.Connection;
import javax.inject.Inject;
import javax.sql.DataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= {"file:src/main/webapp/WEB-INF/spring/**/*-context.xml"})
public class MybatisTest {
@Inject
private DataSource dataSource;
@Test
public void testDataSource() throws Exception{
try(Connection conn = dataSource.getConnection()) {
System.out.println(conn);
} catch(Exception e) {
e.printStackTrace();
}
}
}
[성공 화면]
3. DB 연동 로그인
3-1. security-context.xml 파일을 수정한다.
이제 DB 연결이 되었기 때문에 하드코딩으로 로그인 셋팅이 되어있던 security-context.xml 파일을 수정한다.
security-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<http auto-config='true' use-expressions="true">
<intercept-url pattern="/favicon.ico" access="hasRole('ROLE_ANONYMOUS')" />
<intercept-url pattern="/resources/**" access="permitAll"/>
<intercept-url pattern="/login/**" access="permitAll" />
<intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />
<form-login login-page="/login/loginPage.do"
login-processing-url="/login.do"
authentication-failure-url="/login/loginPage.do?err=true"
default-target-url="/"
username-parameter="userid"
password-parameter="userpw" />
<session-management>
<concurrency-control max-sessions="1" expired-url="/" />
</session-management>
<access-denied-handler error-page="/login/accessDenied.do" />
</http>
<authentication-manager>
<authentication-provider>
<!-- <user-service>
<user name="admin" password="1234" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="guest" password="1234" authorities="ROLE_USER" />
<user name="guest2" password="a1234" authorities="ROLE_USER" />
</user-service> -->
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="SELECT USERID as username,
PASSWORD,
1 as enabled
FROM TEST_USER WHERE USERID=?"
authorities-by-username-query="SELECT USERID as username,
CASE WHEN USERGRADE='01' THEN 'ROLE_ADMIN'
ELSE 'ROLE_USER'
END authority
FROM TEST_USER WHERE USERID=?"
/>
</authentication-provider>
</authentication-manager>
</beans:beans>
- <jdbc-user-service data-source-ref="dataSource" /> : root-context.xml에서 설정한 bean id를 가져온다.
- user-by-username-query : 사용자 인증을 가져온다. username, password, enabled 컬럼은 필수이다. 이는 스프링에서 지정된 컬럼명이며 만일 테이블 커럶과 다른 경우 별칭(AS)을 줘서 변경하도록 한다. Enabled 필드 값이 0이면 비활성이고 1이면 활성을 의미한다.
- authorities-by-username-query : 권한 인증을 가져온다. Authority 필드 값은 "ROLE_ADMIN" 이나 "ROLE_USER"가 오도록 한다.
*TEST_USER 라는 테이블에는 현재 아래와 같은 데이터가 들어있다.
CASE문을 통해 USERGRADE가 01이면 'ROLE_ADMIN' 아니면 'ROLE_USER'로 셋팅을 했다.
3-2. 테스트
4. Mybatis 연동 데이터 조회
4-1. Mybatis 설정 파일을 생성한다.
[src]-[resources] 경로에 mybatis-config.xml 파일을 추가한다.
MyBatis 설정 파일을 이용하면 스프링 설정 파일과 별도로 관리를 할 수 있어 분리시켜주는 것이 좋다.
이후에 mapper 파일도 만들어야 하기 때문에 MarketPlace에서 MyBatipse 1.2.2 파일을 내려받는다.
[Help]-[Eclipse Marketplace]
중간에 warning 창이 뜬다면 Install anyway를 클릭하여 넘어간다.
설치가 완료되면 resource에서 마우스 우클릭 후 [New]-[Other...]를 클릭
[MyBatis]-[MyBatis XML Mapper] 클릭.
mybatis-config.xml 파일 생성
파일 생성 후 아래와 같이 입력한다.
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias alias="LoginVO" type="com.company.login.dto.LoginVO"/>
</typeAliases>
</configuration>
<typeAlias alias="LoginVO" type="com.company.login.dto.LoginVO"> : alias는 mapper(SQL문 관리)에 resultType 경로를 작성하는데 불편함을 해소시켜주는 별칭이다.
곧 vo와 mapper 를 만들면서 이들이 어떤 관계인지 이해가 될 것이다.
프로젝트 구조에 대해 먼저 언급하자면 가장 보편적인 dao, vo, service controller로 구현할 것이다.
*프로젝트 구조
4-2. mapper 파일 인식시키기.
root-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:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<!-- 데이터베이스 연결 -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<!-- <property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"/> -->
<property name="url" value="jdbc:oracle:thin:@접속 DB 정보" />
<!-- <property name="url" value="jdbc:log4jdbc:oracle:thin:@접속 DB 정보" /> -->
<property name="username" value="접속DB 아이디"/>
<property name="password" value="접속DB 패스워드"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:/mybatis-config.xml"></property>
<property name="mapperLocations" value="classpath:/sqlmap/**/*.xml"></property>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
</beans>
- <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> : MyBatis 설정파일을 바탕으로 SqlSessionFactory를 생성한다.
- <property name="configLoaction" value="classpath:/mybatis-config.xml"></property> : 4-1에서 생성한 Mybatis 설정 파일의 위치를 지정해준다.
- <property name="mapperLoactions" value="classpath:/sqlmap/**/*.xml"></property> : mapper에 관련된 자원의 위치를 나열한다. 이 프로퍼티는 Mybatis의 mapper 파일들의 위치를 지정하기 위해 사용되고 디렉터리 아래 모든 파일을 로드하기 위해 "**" 패턴을 사용할수도 있고, 가장 상위 위치를 지정하는 것으로 재귀적으로 하위 경로를 찾도록 할 수도 있다. 여기서는 sqlmap 디렉토리 아래 모든 폴더의 파일을 찾을 수 있도록 했다. (classpath: 경로가 의미하는 것은 src - resources 이다)
- <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" destory-method="clearCache"> : SqlSessionTemplate은 Mybatis와 스프링 연동모듈의 핵심이다. SqlSessionTemplate은 SqlSession을 구현하고 코드에서 SqlSession를 대체하는 역할을 한다. SQL문을 실행하는 역할
- <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" /> : SqlSessionTemplate 클래스 생성자에 상위에서 만든 sqlSessionFactory를 주입시켜준다.
* 주석 처리된 driverClassName과 url은 현재 포스팅에는 없지만 log4j를 사용하여 추가되었습니다.
4-3. vo 생성
LoginVO.java
package com.company.login.dto;
public class LoginVO {
private String cpcode;
private String cpstf;
private String username;
public String getCpcode() {
return cpcode;
}
public void setCpcode(String cpcode) {
this.cpcode = cpcode;
}
public String getCpstf() {
return cpstf;
}
public void setCpstf(String cpstf) {
this.cpstf = cpstf;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
4-4. mapper 파일 생성
SQL문과 관련된 mapper 파일은 [sqlmap]-[login] 폴더에 만들었다.(경로 : [src]-[resources]-[sqlmap])
login_sql.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="login">
<select id="getInfo" resultType="LoginVO" >
SELECT CPCODE
, CDSTF
, USERNAME
FROM TEST_USER
WHERE USERID='admin'
</select>
</mapper>
Mybatis를 통해 반환된 결과값은 vo를 통해 받게 된다. 이 때 resultType을 LoginVO로 지정했다. LoginVO는 앞서 4-1에서 Mybatis 설정 파일을 만들면서 지정했던 alias 타입이다.
만약 alias 지정을 해주지 않는다면 com.company.login.dto.LoginVO 경로를 직접 입력해주어야 한다. 때문에 패키지명이 길어지게 되면 매번 긴 경로를 입력해야하는 불편함이 생긴다.
4-5. service 생성
LoginService.java
package com.company.login.service;
import com.company.login.dto.LoginVO;
public interface LoginService {
public LoginVO getInfo();
}
4-6. serviceImpl 생성
LoginServiceImpl.java
package com.company.login.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.company.login.dao.LoginDAO;
import com.company.login.dto.LoginVO;
import com.company.login.service.LoginService;
@Repository
public class LoginServiceImpl implements LoginService {
@Autowired
private LoginDAO loginDAO;
@Override
public LoginVO getInfo(){
return loginDAO.getInfo();
}
}
4-7. dao 생성
LoginDAO.java
package com.company.login.dao;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.company.login.dto.LoginVO;
@Repository("LoginDAO")
public class LoginDAO {
@Autowired
private SqlSession mybatis;
public LoginVO getInfo(){
return mybatis.selectOne("login.getInfo");
}
}
4-8. controller 생성(수정)
LoginController.java
package com.company.login.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.company.login.service.LoginService;
@Controller
public class LoginController {
@Autowired
LoginService loginService;
@RequestMapping(value="/login/loginPage.do")
public String loginPage() {
return "/login/loginPage";
}
@RequestMapping(value="/login/accessDenied.do")
public String accessDeniedPage() throws Exception {
return "/login/accessDenied";
}
}
컨트롤러는 사실 지난 포스팅(스프링 시큐리티 - 3) 커스터마이징)에서 만들었기 때문에 변경된 내용은 없다. 다만 로그인 성공시 이동되는 HomeController.java에서 vo에 담긴 데이터를 가져올 수 있도록 코드를 추가했다.
HomeController.java
package com.company.board;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.company.login.dto.LoginVO;
import com.company.login.service.LoginService;
/**
* Handles requests for the application home page.
*/
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
@Autowired
LoginService loginService;
/**
* Simply selects the home view to render by returning its name.
*/
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
LoginVO loginVO = new LoginVO();
loginVO = loginService.getInfo();
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate );
model.addAttribute("cpcode", loginVO.getCpcode());
model.addAttribute("cpstf", loginVO.getCpstf());
model.addAttribute("username", loginVO.getUsername());
return "home";
}
}
4-9. 화면 수정
home.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<html>
<head>
<title>Home</title>
</head>
<body>
<h1>
Hello world!
</h1>
<P> The time on the server is ${serverTime}. </P>
회사코드 : ${cpcode } <br>
사 번 : ${cpstf } <br>
이 름 : ${username }
</body>
</html>
5. 테스트
'ROLE_ADMIN' 계정으로 접속해보자.
성공!
여기까지 스프링 시큐리티 설정과 커스터마이징, Mybatis+DB 연동이 완료되었습니다.
+ 피드백은 언제나 환영입니다 :)
'Framework > Spring' 카테고리의 다른 글
[Error]Ajax 404 에러 (2) | 2020.03.09 |
---|---|
[Error]net::ERR_ABORTED 404 (0) | 2020.03.05 |
[Spring Framework]Spring Security 적용하기 - 3) 커스터마이징 (6) | 2019.10.23 |
[Spring Framework]Spring Security 적용하기 - 2) 스프링 시큐리티 적용 (1) | 2019.10.23 |
[Spring Framework]Spring Security 적용하기 - 1) 스프링 프레임워크 설정 (0) | 2019.10.23 |
소중한 공감 감사합니다.