Spring 실습

Spring 실습 18일차(시큐리티)

choco2706 2024. 5. 17. 21:08

접근 거부

시큐리티 패키지와 CustomAccessDeniedHandler 클래스를 만들어준다.

package kr.or.ddit.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class CustomAccessDeniedHandler implements AccessDeniedHandler {

	@Override
	public void handle(HttpServletRequest request, HttpServletResponse response,
			AccessDeniedException accessDeniedException) throws IOException, ServletException {
		log.info("handle");
		
		// DB 작업
		// 로그 작업
		// 메세지 발송
		
		response.sendRedirect("/accessError");
		
		/*
		    공지사항 등록 화면(/notice/register)은 
		    일반회원(member/java)이 접근할 수 없는 페이지이고,
		    관리자(admin/java)만 접근 가능하므로..
		    지정된 접근 거부 처리자(CustomAccessDeniedHander)에서 
		    접근 거부 처리 페이지(/accessError)로 리다이렉트 시킴
	    */
		
	}

}

 

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">

  <!-- CustomAccessDeniedHandler customAccessDenied = 
     new CustomAccessDeniedHandler();
-->
  <bean id="customAccessDeniedHandler" 
    class="kr.or.ddit.security.CustomAccessDeniedHandler"></bean>

  <security:http>
<!--          <security:csrf disabled="true"/> -->
     <!-- URI 패턴으로 접근 제한을 설정함 -->
     <security:intercept-url pattern="/board/list" access="permitAll"/>                   <!-- access="permitAll" : 누구나 접근가능 -->
     <security:intercept-url pattern="/board/register" access="hasRole('ROLE_MEMBER')"/>      <!-- 회원만 접근가능 -->
     <security:intercept-url pattern="/notice/list" access="permitAll"/>                  <!-- access="permitAll" : 누구나 접근가능 -->
     <security:intercept-url pattern="/notice/register" access="hasRole('ROLE_ADMIN')"/>      <!-- 관리자만 접근가능 -->


     <!-- 폼 기반 인증 기능을 사용 -->
     <security:form-login />

     <!-- 접근 거부 처리자의 URI 지정 -->
<!--          <security:access-denied-handler error-page="/accessError"/> -->

    <!-- 등록한 사용자 정의 bean을 접근 거부 처리자로 지정함 -->
    <security:access-denied-handler ref="customAccessDeniedHandler"/>

  </security:http>

  <!-- 지정된 아이디와 패스워드로 로그인이 가능하도록 설정-->
   <!-- 스프링 시큐리니티 5부터 기본적으로 PasswordEncoder를 지정해야 하는데,
      그 이유는 사용자 테이블(USERS)에 비밀번호를 암호화하여 저장해야 하므로..
      우리는 우선 비밀번호를 암호화 처리 하지 않았으므로
      암호화 하지 않는 PasswordEncoder를 직접 구현하여 지정하기로 함
      noop : no option password
    -->

  <!-- authentication : 인증 (로그인) -->
  <security:authentication-manager>
      <!-- 지정된 아이디와 패스워드로 로그인이 가능하도록 설정함 -->
      <security:authentication-provider>
            <security:user-service>
                <security:user name="member" password="{noop}1234" authorities="ROLE_MEMBER" />
                <security:user name="admin" password="{noop}1234" authorities="ROLE_MEMBER,ROLE_ADMIN" />
            </security:user-service>
      </security:authentication-provider>
  </security:authentication-manager>

</beans>
<bean id="customAccessDeniedHandler" 
class="kr.or.ddit.security.CustomAccessDeniedHandler"></bean>

 

class의 위치에 있는 class파일을 bean으로 등록해 메모리에 올림

 

권한이 없는 아이디

 

notice / register는 admin권한이 있는 아이디만 접근이 가능하다.

로그인 시도 결과

 

여기까지는 어제 했던 방식과 크게 다르지 않다.

 

 

로그인 화면

<!-- 폼 기반 인증 기능을 사용 -->
<!--          <security:form-login /> -->
<security:form-login login-page="/login" />

 

이전에 만들어놓은 form-login은 주석처리를 해준 뒤 위 코드를 작성해준다

시큐리티의 로그인이 필요한 경우 /login 경로를 호출한다

 

controller패키지에 위 호출을 받아줄 LoginController를 만들어준다.

package kr.or.ddit.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Controller
public class LoginController {
	
	@GetMapping("/login")
	public String loginForm(Model model) {
		log.info("loginForm");
		
		return "loginForm";
	}
}

 

views에 loginForm.jsp를 만들어준다.

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
<center>
	<div class="login-box">
	   <div class="card center">
	      <div class="card-body login-card-body">
	         <p class="login-box-msg">Sign in to start your session</p>
	         <form action="/login" method="post">
	            <div class="input-group mb-3">
	               <input type="text" name="userName" id="userName" class="form-control" placeholder="아이디">
	               <div class="input-group-append">
	                  <div class="input-group-text">
	                     <span class="fas fa-envelope"></span>
	                  </div>
	               </div>
	            </div>
	            <div class="input-group mb-3">
	               <input type="password" name="password" id="password" class="form-control" placeholder="비밀번호">
	               <div class="input-group-append">
	                  <div class="input-group-text">
	                     <span class="fas fa-lock"></span>
	                  </div>
	               </div>
	            </div>
	            <div class="row">
	               <div class="col-8">
	                  <div class="icheck-primary">
	                     <input type="checkbox" id="remember"> <label
	                        for="remember"> Remember Me </label>
	                  </div>
	               </div>
	               <div class="col-4">
	                  <button type="submit" class="btn btn-primary btn-block">Sign
	                     In</button>
	               </div>
	            </div>
	            <!-- csrf : Cross Site Request Forgery -->
	            <sec:csrfInput  />
	         </form>
	      </div>
	   </div>
	</div>
</center>

 

<center>태그는 안에 들어있는 코드들을 화면 가운데로 위치시킨다.

변경된 로그인 창

 

위 jsp코드에서 중요한 점은 각 id와 password의 name값이 username, password로 작성되어야 한다는 점이다.

 

또한 sec태그를 사용한 코드가 있어야 post방식으로 처리가 가능하다. 자세한 내용은 후술

로그인 시도
로그인 성공

 

 

 

로그인 성공 처리

security-context.xml에 로그인 성공을 위한 클래스를 bean으로 등록하겠다는 코드를 추가해주고

<bean id="customLoginSuccess" 
class="kr.or.ddit.security.CustomLoginSuccessHandler"></bean>

 

security 패키지에 위 경로의 이름대로 CustomLoginSuccessHandler 클래스를 만들어준다.

package kr.or.ddit.security;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;

import lombok.extern.slf4j.Slf4j;

/*   /notice/register -> loginForm -> 로그인 -> CustomLoginSuccessHandler(성공)
-> 사용자 작업.. -> /notice/register 로 리다이렉트 해줌
(스프링 시큐리티에서 기본적으로 사용되는 구현 클래스) 
*/

@Slf4j
public class CustomLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

	// 부모 클래스의 메소드를 재정의
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication auth)
			throws ServletException, IOException {

		log.info("onAuthenticationSuccess");

		// auth.getPrincipal() : 사용자 정보를 가져옴
		// 시큐리티에서 사용자 정보는 User 클래스의 객체로 저장됨(CustomUser.java를 참고)
		User customUser = (User) auth.getPrincipal();
		
		// 사용자의 Id 리턴
		log.info("username : " + customUser.getUsername());
		
		//auth.getAuthorities() -> 권한들(ROLE_MEMBER,ROLE_ADMIN)
        //authority.getAuthority() : ROLE_MEMBER 
		List<String> roleNames = new ArrayList<String>();
		auth.getAuthorities().forEach(authority->{
			roleNames.add(authority.getAuthority());
		});
		
		log.info("roleNames : " + roleNames);
		
		// 부모에게 줌
		super.onAuthenticationSuccess(request, response, auth);

	}
}

이클립스 콘솔에 로그인한 Id와 해당 아이디의 보유 권한이 찍힌다.

 

 

로그아웃

security-context.xml에 따로 추가를 해주어야 한다. /logout페이지가 따로 필요하진 않음

<!-- 로그아웃 처리를 위한 URI를 지정하고, 로그아웃한 후에 세션을 무효화함 
/logout : post방식, 요청 URI = form의 action="logout" -->
<security:logout logout-url="/logout" invalidate-session="true" />

 

 

우선 로그아웃을 하려면 로그인이 되어있어야 하고, 로그인을 했는지 확인이 되어야 하기 때문에 tiles폴더의 aside에서 

 

이 부분이 로그인 하면 로그인한 회원으로 변하도록 만들어보겠다.

 

우선 하단의 코드를 aside에 덮어씌워준다.

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
 <!-- Sidebar user panel (optional) -->
    <!-- /// 로그인 안 함(true) 시작/// -->
    <sec:authorize access="isAnonymous()">
       <div class="user-panel mt-3 pb-3 mb-3 d-flex">
         <div class="image">
           <img src="/resources/adminlte/dist/img/user2-160x160.jpg" class="img-circle elevation-2" alt="User Image">
         </div>
         <div class="info">
           <a href="#" class="d-block">Alexander Pierce</a>
         </div>
       </div>
   </sec:authorize>
    <!-- /// 로그인 안 함 끝/// -->
    
    <!-- /// 로그인 함 시작 /// -->
    <sec:authorize access="isAuthenticated()">
<%--        <sec:authentication property="principal" /> --%>
       <div class="user-panel mt-3 pb-3 mb-3 d-flex">
         <div class="image">
           <img src="/resources/upload/2024/05/14/7fc538d6-999e-4067-a02e-aa4552495802_A007.jpg" class="img-circle elevation-2" alt="User Image">
         </div>
         <div class="info">
           <a href="#" class="d-block">개똥이님 환영합니다.</a>
           <form action="/logout" method="post">
              <button type="submit" class="btn btn-block btn-secondary btn-xs">로그아웃</button>
              <sec:csrfInput />
           </form>
         </div>
       </div>
    </sec:authorize>
    <!-- /// 로그인 함 끝 /// -->

 

로그인에 성공했다면 아래 코드 // 로그인 함 // 부분이 실행되고

 

로그아웃을 할 시 로그인 창으로 넘어가며 // 로그인 안 함 // 부분이 실행된다.

 

 

JDBC를 이용한 인증 / 인가

아래 코드를 사용하여 테이블들과 데이터를 입력해주자

CREATE TABLE USERS(
    USERNAME VARCHAR2(150),
    PASSWORD VARCHAR2(150),
    ENABLED VARCHAR2(1),
    CONSTRAINT PK_USERS PRIMARY KEY(USERNAME)
);
        
CREATE TABLE AUTHORITIES(
    USERNAME VARCHAR2(150),
    AUTHORITY VARCHAR2(150),
    CONSTRAINT PK_AUTH PRIMARY KEY(USERNAME, AUTHORITY),
    CONSTRAINT FK_AUTH FOREIGN KEY(USERNAME) REFERENCES USERS(USERNAME)
);

INSERT INTO USERS(USERNAME, PASSWORD)
SELECT EMP_NO, 'java' FROM EMPLOYEE;

INSERT INTO AUTHORITIES(USERNAME, AUTHORITY)
SELECT EMP_NO, 'ROLE_MAMBER' FROM EMPLOYEE;

SELECT A.USERNAME, A.PASSWORD, A.ENABLED, B.USERNAME, B.AUTHORITY
FROM USERS A, AUTHORITIES B
WHERE A.USERNAME = B.USERNAME;

실행 결과

 

username이 id, password가 비밀번호로 작동.

authority가 권한으로써 작동하도록 만들어야 한다.

security-context.xml 수정

<!-- 비밀번호 암호화 처리기를 빈으로 등록 -->
<bean id="passwordEncoder" 
  class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>

<!-- authentication : 인증 (로그인) -->
	<security:authentication-manager>
		<!-- 지정된 아이디와 패스워드로 로그인이 가능하도록 설정함 -->
		<security:authentication-provider>
			<!-- root-context.xml에 있음 -->
			<security:jdbc-user-service data-source-ref="dataSource" />
			<!-- 비밀번호 암호화 -->
			<security:password-encoder ref="passwordEncoder"/>
<!-- 			<security:user-service> -->
<!-- 				<security:user name="member" password="{noop}1234" -->
<!-- 					authorities="ROLE_MEMBER" /> -->
<!-- 				<security:user name="admin" password="{noop}1234" -->
<!-- 					authorities="ROLE_MEMBER,ROLE_ADMIN" /> -->
<!-- 			</security:user-service> -->
		</security:authentication-provider>
	</security:authentication-manager>

 

이전에 작성한 security:user-service를 주석처리 하고 대신 아래 코드로 변경되었다.

<!-- root-context.xml에 있음 -->
<security:jdbc-user-service data-source-ref="dataSource" />
<!-- 비밀번호 암호화 -->
<security:password-encoder ref="passwordEncoder"/>

 

위 코드는 데이터베이스의 비밀번호인 java를 암호화 하겠다는 코드이다.

 

LoginController 수정

package kr.or.ddit.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Controller
public class LoginController {
	
	//DI / IoC
    //passwordEncoder 객체는 security-context.xml의 bean에 등록되어 있음
	@Autowired
	PasswordEncoder passwordEncoder;
	
	@GetMapping("/login")
	public String loginForm(Model model) {
		log.info("loginForm");
		
		String pwd = "java";
		
		String encodePwd = this.passwordEncoder.encode(pwd);
		log.info("encodePwd : " + encodePwd);
		
		return "loginForm";	
	}
}

notice / register로 접속하면 pwd가 암호화되서 나온다.

 

 

PasswordEncoder란?

더보기

Spring Security에서는 비밀번호를 안전하게 저장할 수 있도록

비밀번호의 단방향 암호화를 지원하는 PasswordEncoder 인터페이스와 구현체들을 제공

 

저 암호화된 비밀번호를 데이터베이스에 넣어준다

 

그 다음 AUTHORITIES 테이블의 아이디 중 하나에 ROLE_ADMIN을 부여해준 뒤 로그인을 해보자

notice / register 로그인

 

비밀번호는 암호화했지만 java로 로그인이 가능하다.

로그인 성공

 

로그인에 성공하여 왼쪽 Alexander Pierce님이 사라지고 개똥이님이 표기된다 

 

EMPLOYEE

이번에는 employee테이블의 정보로 로그인을 시도해보려 한다.

 

미리 만들어놓았던 employee 테이블에 emp_pwd(비밀번호)와 enabled(탈퇴여부, 기본값 = '1'(문자열))를 추가해준다.

 

비밀번호에 아까 암호화한 java를 넣어주고 다시 설정에 들어가 NotNull 설정을 해주자

(데이터가 없는 상태에서 NotNull 설정하면 적용되지 않는다.)

데이터 입력 예시

 

그리고 EMPLOYEE_AUTH 테이블을 만들어준다.

 

EMP_NO는 외래키로 등록해야 하므로 EMPLOYEE테이블의 EMP_NO와 데이터 유형과 크기가 같아야 한다.

복합키 설정
외래 키 설정

 

위 설정까지 마쳐주자

 

더보기
CREATE EMPLOYEE_AUTH(
    EMP_NO VARCHAR2(6),
    AUTH VARCHAR2(100),
    CONSTRAINT PK_EMPLOYEE_AUTH PRIMARY KEY(EMP_NO, AUTH),
    CONSTRAINT FK_EMPLOYEE_AUTH FOREIGN KEY(EMP_NO)
            REFERENCES EMPLOYEE(EMP_NO)
)

 

CREATE문을 작성하면 이렇게 될 것이다.

 

INSERT INTO EMPLOYEE_AUTH(EMP_NO, AUTH)
SELECT EMP_NO, 'ROLE_MEMBER'
FROM EMPLOYEE;

 

위 코드까지 실행해주면 EMP_NO당 권한이 부여된다.

SELECT A.EMP_NO, A.EMP_NAME, A.EMP_ADDRESS, A.EMP_TELNO, A.EMP_SALARY, 
        A.FILENAME, A.EMP_PWD, A.ENABLED
        ,B.EMP_NO, B.AUTH
FROM EMPLOYEE A, EMPLOYEE_AUTH B
WHERE A.EMP_NO = B.EMP_NO
AND A.EMP_NO = 'A004';

EMP_NO가 A004인 사원의 AUTH 테이블 조회

 

 

SELECT한 결과를 시큐리티에 넘겨주자

 

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">

	<!-- CustomAccessDeniedHandler customAccessDenied = new CustomAccessDeniedHandler(); -->
	<bean id="customAccessDeniedHandler"
		class="kr.or.ddit.security.CustomAccessDeniedHandler"></bean>

	<bean id="customLoginSuccess"
		class="kr.or.ddit.security.CustomLoginSuccessHandler"></bean>

	<!-- 비밀번호 암호화 처리기를 빈으로 등록 -->
	<bean id="passwordEncoder"
		class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>

	<bean id="customUserDetailsService"
		class="org.springframework.security.CustomUserDetailsService"></bean>

	<security:http>
		<!-- <security:csrf disabled="true"/> -->
		<!-- URI 패턴으로 접근 제한을 설정함 -->
		<security:intercept-url pattern="/board/list"
			access="permitAll" />                   <!-- access="permitAll" : 누구나 접근가능 -->
		<security:intercept-url
			pattern="/board/register" access="hasRole('ROLE_MEMBER')" />      <!-- 회원만 접근가능 -->
		<security:intercept-url pattern="/notice/list"
			access="permitAll" />                  <!-- access="permitAll" : 누구나 접근가능 -->
		<security:intercept-url
			pattern="/notice/register" access="hasRole('ROLE_ADMIN')" />      <!-- 관리자만 접근가능 -->


		<!-- 폼 기반 인증 기능을 사용 -->
		<!-- <security:form-login /> -->
		<security:form-login login-page="/login"
			authentication-success-handler-ref="customLoginSuccess" />

		<!-- 로그아웃 처리를 위한 URI를 지정하고, 로그아웃한 후에 세션을 무효화함 /logout : post방식, 요청 URI 
			= form의 action="logout" -->
		<security:logout logout-url="/logout"
			invalidate-session="true" />

		<!-- 접근 거부 처리자의 URI 지정 -->
		<!-- <security:access-denied-handler error-page="/accessError"/> -->

		<!-- 등록한 사용자 정의 bean을 접근 거부 처리자로 지정함 -->
		<security:access-denied-handler
			ref="customAccessDeniedHandler" />

	</security:http>

	<!-- 지정된 아이디와 패스워드로 로그인이 가능하도록 설정 -->
	<!-- 스프링 시큐리니티 5부터 기본적으로 PasswordEncoder를 지정해야 하는데, 그 이유는 사용자 테이블(USERS)에 
		비밀번호를 암호화하여 저장해야 하므로.. 우리는 우선 비밀번호를 암호화 처리 하지 않았으므로 암호화 하지 않는 PasswordEncoder를 
		직접 구현하여 지정하기로 함 noop : no option password -->

	<!-- authentication : 인증 (로그인) -->
	<security:authentication-manager>
		<!-- 지정된 아이디와 패스워드로 로그인이 가능하도록 설정함 -->
		<security:authentication-provider
			user-service-ref="customUserDetailsService">
			<!-- root-context.xml에 있음 -->
			<!-- <security:jdbc-user-service data-source-ref="dataSource" /> -->
			<!-- 비밀번호 암호화 -->
			<security:password-encoder
				ref="passwordEncoder" />
			<!-- <security:user-service> -->
			<!-- <security:user name="member" password="{noop}1234" -->
			<!-- authorities="ROLE_MEMBER" /> -->
			<!-- <security:user name="admin" password="{noop}1234" -->
			<!-- authorities="ROLE_MEMBER,ROLE_ADMIN" /> -->
			<!-- </security:user-service> -->
		</security:authentication-provider>
	</security:authentication-manager>

</beans>

 

변경된 부분은

	<bean id="customUserDetailsService"
		class="org.springframework.security.CustomUserDetailsService"></bean>

	<!-- authentication : 인증 (로그인) -->
	<security:authentication-manager>
		<!-- 지정된 아이디와 패스워드로 로그인이 가능하도록 설정함 -->
		<security:authentication-provider
			user-service-ref="customUserDetailsService">
			<!-- root-context.xml에 있음 -->
			<!-- <security:jdbc-user-service data-source-ref="dataSource" /> -->
			<!-- 비밀번호 암호화 -->
			<security:password-encoder
				ref="passwordEncoder" />
			<!-- <security:user-service> -->
			<!-- <security:user name="member" password="{noop}1234" -->
			<!-- authorities="ROLE_MEMBER" /> -->
			<!-- <security:user name="admin" password="{noop}1234" -->
			<!-- authorities="ROLE_MEMBER,ROLE_ADMIN" /> -->
			<!-- </security:user-service> -->
		</security:authentication-provider>
	</security:authentication-manager>

 

customerUserDetailsService라는 클래스를 bean으로 등록한다.라는 코드를 작성해줬다.

 

위 코드가 최종 프로젝트까지 쓸 최종 security-context.xml일 것이다.

 

security 패키지에 CustomUserDetailsService 클래스를 만들어준다.

interface의 add를 눌러 UserDetailsService interface를 검색해 추가하고 만들어야한다

 

UserDetailsService란?

더보기

Spring Security에서 사용자의 정보를 담는 인터페이스이다.

기본 오버라이드 메서드들

interface를 add해서 만들어주자

 

package kr.or.ddit.security;

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 lombok.extern.slf4j.Slf4j;

/*
UserDetailsService : 스프링 시큐리티에서 제공해주고 있는
사용자 상세 정보를 갖고 있는 인터페이스
 */
@Slf4j
@Service
public class CustomUserDetailsService implements UserDetailsService {
	//DI(Dependency Injection) : 의존성 주입
    //IoC(Inversion of Control) : 제어의 역전
	@Autowired
	private EmployeeMapper employeeMapper;

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
   	 	//1) 사용자 정보를 검색
        //username : 로그인 시 입력 받은 회원의 아이디. <input type="text" name="username"
		log.info("CustomUserDetailsService >>> " + username);
		
		return null;
	}

}

 

아마 EmployeeMapper를 만든 적이 없을 것이니 Mapper 패키지에 만들어주자

package kr.or.ddit.mapper;

import kr.or.ddit.vo.EmployeeVO;

public interface EmployeeMapper {

	public EmployeeVO detail(String username);
	
}

 

employee2_SQL.xml을 만들고 namespace를 mapper의 경로대로 적어준다

<?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="kr.or.ddit.mapper.EmployeeMapper">

	<resultMap type="employeeVO" id="employeeMap">
		<result property="empNo" column="EMP_NO"/>
		<result property="empName" column="EMP_NAME"/>
		<result property="empAddress" column="EMP_ADDRESS"/>
		<result property="empTelno" column="EMP_TELNO"/>
		<result property="empSalary" column="EMP_SALARY"/>
		<result property="filename" column="FILENAME"/>
		<result property="empPwd" column="EMP_PWD"/>
		<result property="enabled" column="ENABLED"/>
		<collection property="employeeAuthVOList" resultMap="employeeAuthMap"></collection>
	</resultMap>
	<resultMap type="employeeAuthVO" id="employeeAuthMap">
		<result property="empNo" column="EMP_NO"/>
		<result property="auth" column="AUTH"/>
	</resultMap>

	<select id="detail" parameterType="String" resultMap="employeeMap">
		SELECT A.EMP_NO, A.EMP_NAME, A.EMP_ADDRESS, A.EMP_TELNO, A.EMP_SALARY, 
		        A.FILENAME, A.EMP_PWD, A.ENABLED
		        ,B.EMP_NO, B.AUTH
		FROM EMPLOYEE A, EMPLOYEE_AUTH B
		WHERE A.EMP_NO = B.EMP_NO
		AND A.EMP_NO = #{username}
	</select>
</mapper>

 

SQL을 JOIN했기 때문에 resultMap으로 사용해야 한다.

 

위 대로 SQL문을 사용하려면 EmployeeVO와 EmployeeAuthVO를 추가로 작성해야한다.

private String empPwd;
private String enabled;

// 중첩된 자바빈
private List<EmployeeAuthVO> employeeAuthVOList;
package kr.or.ddit.vo;

import lombok.Data;

@Data
public class EmployeeAuthVO {
	private String empNo;
	private String auth;
}

 

이제 employee테이블에 있는 아이디 중 ADMIN속성이 있는 아이디로 로그인을 시도해보자

나는 A004가 두 권한을 가지고 있다.

 

비밀번호의 경우 암호화를 했지만 기본적으로 java이기 때문에 그대로 사용하면 된다.

로그인 시도
시도 결과

 

위 CustomUserDetailsService에서 데이터를 처리한 후 return값이 null이기 때문에 다시 로그인 화면으로 돌아오는것이 정상적이다.

 

일단은 로그인이 성공한 것

데이터는 잘 받아져오고있다.

 

 

<sec:csrfInput/>

<!-- csrf : Cross Site Request Forgery -->
<sec:csrfInput/>

 

시큐리티를 적용하고 있을 때 전송 방식은 GET방식밖에 사용할 수 없다. 나머지(POST, PUT ... )등을 사용하기 위해는 토큰 설정을 해주어야 하는데, 위 코드가 그 역할을 해주고 있다.

 

자동으로 설정해주기 때문에 다른 코드 필요없이 한줄로 사용 가능하다.

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>

 

위 JSTL이 있어야 sec태그를 사용할 수 있다.

 

 

18일차 종료