스프링 프레임워크 (32)

반응형

 

자동 로그인

 

: 로그인하면 특정 시간 동안 다시 로그인 할 필요가 없는 기능 (쿠키를 사용)

 

 

테이블 생성

--DDL : auto commit;
CREATE TABLE PERSISTENT_LOGINS(
    USERNAME VARCHAR2(200), 
    SERIES VARCHAR2(200),
    TOKEN VARCHAR2(200),
    LAST_USED DATE,
    CONSTRAINT PK_PL PRIMARY KEY(SERIES)
);

 

 

security-context.xml

</security:http> 안의 로그아웃 정보 변경

data-source-ref - db 연결하는 정보와 연결함

 

security:logout 에서 delete-cookies를 설정
security:remember-me에서 DB 정보 기입 후 쿠키 유효시간 설정 => 자동으로 PERSISTENT_LOGINS 테이블에 들어감(테이블의 이름과 컬럼 갯수 등 수정하면 안 됨. 자체적으로 제공해주는 기능이기에 그대로 사용해야함)

<!-- dataSource를 통해 지정한 Database의 
약속된 테이블(persistent_logins) 를 이용하여 기존 로그인 정보를 기록함 

token-validity-seconds : 쿠키의 유효시간(초) 7일로 설정
-->
<security:remember-me data-source-ref="dataSource"
    token-validity-seconds="604800"
/>

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

로그아웃을 하면 자동 로그인에 사용된 쿠키도 함께 삭제해 줌.
-->
<security:logout logout-url="/logout" invalidate-session="true" 
    delete-cookies="remember-me,JSESSION_ID" />

 

 

loginForm.jsp

자동 로그인 부분 수정

name을 remember-me로 기입 (이름은 고정!)

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

<div class="login-box" style="margin:auto;">
	<div class="card">
		<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">
					<!-- 아이디 
					name="username" 으로 꼭 써야함 -->
					<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">
				<!-- 비밀번호
				name="password" 으로 꼭 써야함 -->
					<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">
							<!-- 로그인 상태 유지 체크박스 
								체크 시 : PERSISTENT_LOGINS 테이블에 로그인 로그 정보가 insert 됨
								name을 remember-me로 해야함
							-->
							<input type="checkbox" name="remember-me" id="remember">
							<label for="remember"> 자동 로그인 </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: 를 사용하기 위해선 상단에 추가해야함
				taglib prefix="sec" uri="http://www.springframework.org/security/tags -->
				<sec:csrfInput/>
			</form>
		</div>
	</div>
</div>

 

결과 화면1 : 로그인 시 추가, 로그아웃 시 삭제됨

 

 

 

  • 쿠키 정보 확인

register.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
//request객체 안에 있는 쿠키들을 확인
Cookie[] cookies = request.getCookies();

out.print("<p>쿠키의 개수 : " + cookies.length + "</p>");
%>

<h2>로그인 한 관리자만 접근 가능</h2>
<h3>/notice/register.jsp</h3>

 

결과 화면2-1 : localhost/notice/register 로 url 들어감

 

결과 화면2-2 : 쿠키 추가된 것을 확인할 수 있음

 

 

 

스프링 시큐리티 애너테이션

 

: 메서드가 실행되기 전에 적용할 접근 정책을 지정 시 사용

 

 

주로 사용하는 애너테이션 : @PreAuthorize

 

변경되는 부분 확인

 

 

 

  • 회원만 접근

security-context.xml
security:intercept-url 전부 주석 처리함

 


servlet-context.xml
beans:beans 에 추가 => security:global-method-securit를 사용하기 위해서 추가한 것

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

 

</beans:beans> 안에 추가

<!-- 스프링 시큐리티 애너테이션을 활성화
- pre-post-annotations="enabled" -> 골뱅이PreAuthorize, 골뱅이PostAuthorize 활성화
  *** PreAuthorize : 특정 메소드를 실행하기 전에 role 체킹
  PostAuthorize : 특정 메소드를 실행한 후에 role 체킹
- secured-annotations="enabled" -> 골뱅이Secured를 활성화
  Secured : 스프링 시큐리티 모듈을 지원하기 위한 애너테이션
-->
<security:global-method-security pre-post-annotations="enabled"
    secured-annotations="enabled"/>

 

 

CustomAccessDeniedHandler.java
sendRedirect 부분 변경

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");
		
		//redirect : 새로운 URI를 요청
//		response.sendRedirect("/accessError");
		response.sendRedirect("/login");
	}
}

 

 

BoardController.java

package kr.or.ddit.controller;

import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import kr.or.ddit.dao.BoardDao;
import kr.or.ddit.vo.BoardVO;
import lombok.extern.slf4j.Slf4j;


@Slf4j
@Controller
@RequestMapping("/board")
public class BoardController {

	@PreAuthorize("hasRole('ROLE_MEMBER')")
	@RequestMapping(value="/register", method=RequestMethod.GET)
	public String registerForm() {
		log.info("registerForm에 왔다");
		
		//ModelAndView가 없음
		//mav.setViewname("board/register") 생략
		
		//forwarding : jsp
		return "board/register";
	}
	
	/*
		요청URI : /board/register
		요청파라미터 : 
		요청방식 : post
	 */
	@RequestMapping(value="/register", method=RequestMethod.POST)
	public String registerPost() {
		log.info("registerPost에 왔다");
		
		//forwarding : /views/success.jsp
		return "success";
	}
}

 

 

CommonExceptionHandler.java
@ControllerAdvice를 삭제

 

 

결과 화면3 : http://localhost/board/register로 입력 시 로그인 화면이 뜸

 

 

 

  • 관리자만 접근

NoticeController.java

@PreAuthorize 추가

package kr.or.ddit.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import lombok.extern.slf4j.Slf4j;

@RequestMapping("/notice")
@Slf4j
@Controller
public class NoticeController {

	//요청 URI : /notice/list
	@GetMapping("/list")
	public String list() {
		//forwarding : jsp
		return "notice/list";
	}
	
	//요청 URI : /notice/register
	@PreAuthorize("hasRole('ROLE_ADMIN')")
	@GetMapping("/register")
	public String register() {
		//forwarding : jsp
		return "notice/register";
	}
}

 

 

register.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
//request객체 안에 있는 쿠키들을 확인
Cookie[] cookies = request.getCookies();

out.print("<p>쿠키의 개수 : " + cookies.length + "</p>");

for(int i =0;i<cookies.length;i++){
   out.print(cookies[i].getName()+":"+cookies[i].getValue()+"<br/>");
}
%>

<h2>로그인 한 관리자만 접근 가능</h2>
<h3>/notice/register.jsp</h3>

 

결과 화면4-1

 

결과 화면4-2

 

 

 

  • 예시

BookController.java

 

하단의 셋 중에 하나 사용 가능

@PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_MEMBER')")
@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_MEMBER')")
@Secured({"ROLE_ADMIN","ROLE_MEMBER"})

 

package kr.or.ddit.controller;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;


import kr.or.ddit.service.BookService;
import kr.or.ddit.utils.ArticlePage;
import kr.or.ddit.vo.BookVO;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Controller
public class BookController {
	
	// 요청URI : /create
	// 요청 파라미터 : 
	// 요청방식 : get
	// 로그인(인증) 한 관리자 또는 회원(인가)만 접근 가능
//	@PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_MEMBER')")
//	@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_MEMBER')")
	@Secured("{'ROLE_ADMIN','ROLE_MEMBER'}")
	@RequestMapping(value = "/create", method=RequestMethod.GET)
	public ModelAndView create() {
		ModelAndView mav = new ModelAndView();
		
		// name : title / value = "도서생성"
		mav.addObject("title", "도서생성");
		// jsp
		mav.setViewName("book/create");
		
		return mav;
	}
	
}

 

결과 화면5-1 : http://localhost/create 로 들어가서 로그인

 

결과 화면5-2

 

 

 

  • 로그인 해야 메소드 접근 가능

EmployeeController.java

class 밖에 추가

// 이 클래스의 모든 메소드에 접근하려면 로그인 된 상태여야 함
@PreAuthorize("isAuthenticated()")

 

결과 화면6-1 : http://localhost/employee/create로 들어올 시 화면

 

결과 화면6-2 : 로그인 한 후 화면

 

 

 

 

 

1. 공지사항 등록 - 로그인 한 관리자만 접근 가능
골뱅이PreAuthorize("hasRole('ROLE_ADMIN')")
골뱅이PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_MEMBER')")

2. 공지사항 등록 - 로그인(인증) 한 관리자 또는 회원(인가)만 접근 가능
골뱅이PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_MEMBER')")   
골뱅이Secured({"ROLE_MEMBER","ROLE_ADMIN"})

3. 공지사항 등록 - 로그인(인증) 한 관리자 이면서 회원(인가)만 접근 가능
골뱅이PreAuthorize("hasRole('ROLE_ADMIN') and hasRole('ROLE_MEMBER')")

4. 로그인한 사용자만 접근 가능(권한과 상관 없음)
골뱅이PreAuthorize("isAuthenticated()")

5. 로그인 안 한 사용자가 접근 가능 -> 로그인 한 사용자는 접근 불가
골뱅이PreAuthorize("isAnonymous()")

6. 누구나 접근 가능(PreAuthorize 생략)

 

 

 

반응형
반응형

 

로그인 성공 처리

 

: 로그인을 성공한 후에 로그인 이력 로그를 기록하는 등의 동작을 하고 싶은 경우 사용함

 

 

security-context.xml

<security:form-login login-page="/login"/>를 수정

<!-- 사용자가 정의한 로그인 페이지의 URI를 지정함
사용자 정의 로그인 성공 처리자를 지정함 -->
<security:form-login login-page="/login" 
    authentication-success-handler-ref="customLoginSuccess"/>

 

<beans/> 안에 추가함

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

 

 

CustomLoginSuccessHandler.java

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 costomUser = (User)auth.getPrincipal();
		
		//사용자의 아이디를 리턴 받기
		log.info("username : " + costomUser.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);
	}

}

 

결과 화면1-1 : http://localhost/notice/register 로 들어올 시 admin / 1234로 로그인

 

결과 화면12-2 : 로그인 성공 후 콘솔창에 찍힘

 

 

 

로그아웃 처리

 

 

security-context.xml

</security:http> 안에 추가

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

 

 

aside.jsp

추가

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

 

변경

	<!-- Sidebar -->
	<div class="sidebar">
		<!-- Sidebar user panel (optional) -->
		<!-- /// 로그인 안함 시작 /// -->
		<!-- isAnonymous() 는 로그인 안 했을 때 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()">
			<!-- principal : 사용자정보 객체 -->
<%-- 			<sec:authentication property="principal"/> --%>
			<div class="user-panel mt-3 pb-3 mb-3 d-flex">
				<div class="image">
					<img src="/resources/upload/mang6.jpg"
						class="img-circle elevation-2" alt="User Image">
				</div>
				<div class="info">
					<a href="#" class="d-block">개똥이님 환영합니다.</a>
					<form action="/logout" method="post"> <!-- 꼭 post여야함 -->
						<button type="submit" class="btn btn-block btn-secondary btn-xs">로그아웃</button>
						<sec:csrfInput/>
					</form>
				</div>
			</div>
		</sec:authorize>
		<!-- /// 로그인 함 끝 /// -->

 

 

결과 화면2 : 로그인 후 화면

 

결과 화면3 : 로그아웃 후 화면

 

 

 

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) -- 다른 테이블을 참조함
);

 

USERS 테이블 정보

 

 

데이터 삽입

기존 테이블에 데이터가 없는 경우 일일히 데이터를 넣는 걸 추천함

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

COMMIT;

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

COMMIT;

 

 

데이터 잘 들어갔는지 확인

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

 

결과 화면4

 

추후 ADMIN 도 테스트하기 위해 데이터를 변경해놓아야함

더보기
AUTHORITIES 테이블

 

 

security-context.xml

</security:authentication-provider> 안에 있는 것 수정

			<!-- jdbc로 해야 db에 있는 걸로 가져올 수 있음 -->
			<!-- 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> -->

 

</beans> 안에 추가

<!-- 비밀번호 암호화 처리글   -->
<bean id="passwordEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>

 

 

LoginController.java

이름이 같아야 함

 

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) {
		
		String pwd = "java";
		
		String encodedPwd = this.passwordEncoder.encode(pwd);
		log.info("encodedPwd : " + encodedPwd);
		
		//forwarding : jsp
		// /views/ + loginForm + ".jsp";
		return "loginForm";
	}
}

 

결과 화면5

 

USERS 테이블에서 PASSWORD를 변경

 

결과 화면6-1

 

결과 화면6-2

 

 

 

 

 

EMPLOYEE 테이블 수정

 

추가한 비밀번호 변경, 전부 JAVA를 암호화한 값으로 넣음

 

EMPLOYEE_AUTH 테이블 설정1

 

EMPLOYEE_AUTH 테이블 설정2

 

EMPLOYEE_AUTH 테이블 설정2

 

 

데이터 삽입

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

COMMIT;

 

+ 하나의 데이터만 ROLE_ADMIN으로 수정해야함

 

 

security-context.xml

<security:authentication-manager> 변경

	<security:authentication-manager>
		<!-- 지정된 아이디와 패스워드로 로그인이 가능하도록 설정함 -->
		<security:authentication-provider user-service-ref="customUserDetailsService"> <!-- 인증 제공자 -->
			<!-- 비밀번호 암호화 -->
			<security:password-encoder ref="passwordEncoder"/>
		</security:authentication-provider>

 

</beans> 안에 추가

<bean id="customUserDetailsService" class="kr.or.ddit.security.CustomUserDetailsService">
</bean>

 

 

CustomUserDetailsService.java

package kr.or.ddit.security;

import org.springframework.beans.factory.annotation.Autowired;
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 kr.or.ddit.mapper.EmployeeMapper;
import kr.or.ddit.vo.EmployeeVO;
import lombok.extern.slf4j.Slf4j;

/*
UserDetailsService : 스프링 시큐리티에서 제공해주고 있는
사용자 상세 정보를 갖고 있는 인터페이스
 */
@Slf4j
@Service
public class CustomUserDetailsService implements UserDetailsService {

	//EMPLOYEE 테이블을 위한 매퍼 인터페이스
	//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 : " + username);
		
		//1명이 로그인을 함
		EmployeeVO employeeVO = this.employeeMapper.detail(username);
		log.info("CustomUserDetailsService->employeeVO : " + employeeVO);
		
		//MVC에서는 Controller로 리턴하지 않고, CustomUser로 리턴함
		//CustomUser : 사용자 정의 유저 정보. extends User를 상속받고 있음
		//2) 스프링 시큐리티의 User 객체의 정보로 넣어줌 => 프링이가 이제부터 해당 유저를 관리
		//User : 스프링 시큐리에서 제공해주는 사용자 정보 클래스
		/*
		memberVO(우리) -> user(시큐리티)
		-----------------
		empNo        		-> username
		empPwd        		-> password
		enabled       		-> enabled
		employeeAuthVOList	-> authorities
		*/
		return employeeVO==null?null:new CustomUser(employeeVO);
	}

}

 

 

EmployeeMapper.java

package kr.or.ddit.security;

import kr.or.ddit.vo.EmployeeVO;

public interface EmployeeMapper {

	public EmployeeVO detail(String username);
	
}

 

 

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

 

 

EmployeeVO.java

package kr.or.ddit.vo;

import java.util.List;

import javax.validation.Valid;

import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.Range;
import org.springframework.web.multipart.MultipartFile;

import lombok.Data;

@Data
public class EmployeeVO {
	@NotBlank(message = "직원 번호를 입력해주세요")
	private String empNo;
	
	@NotBlank(message = "직원 명을 입력해주세요")
	private String empName;
	
	@NotBlank(message = "주소를 입력해주세요")
	private String empAddress;
	
	@NotBlank(message = "연락처를 입력해주세요")
	private String empTelno;
	
	@Range(min=1,message="급여를 입력해주세요.")
	private int empSalary;

	
	private String filename;
	private String empPwd;
	private String enabled;
	
	
	@Valid
	private List<LicenseVO> licenseVOList;
	
	//중첩된 자바빈 컬렉션
	private List<EmployeeAuthVO> employeeAuthVOList;
	
	//EMPLOYEE : 증명사진 = 1 : 1
	private MultipartFile uploadFile;
}

 

 

EmployeeAuthVO.java

package kr.or.ddit.vo;

import lombok.Data;

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

 

 

mybatisAlias.xml

</typeAliases> 안에 추가

<typeAlias type="kr.or.ddit.vo.EmployeeAuthVO" alias="employeeAuthVO"/>

 

 

CustomUser.java

CustomUser 세팅, Set<GrantedAuthority>로 값이 들어감

 

package kr.or.ddit.security;

import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;

import kr.or.ddit.vo.EmployeeVO;

//User : 스프링 시큐리티의 사용자 정보를 관리하는 사용자 최상위 클래스
public class CustomUser extends User {
	/* User 클래스의 프로퍼티
	private String password;
	private final String username;
	private final Set<GrantedAuthority> authorities;
	private final boolean accountNonExpired;
	private final boolean accountNonLocked;
	private final boolean credentialsNonExpired;
	private final boolean enabled;
	*/
	private EmployeeVO employeeVO;
	
	//User의 생성자를 처리해줌
	public CustomUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
		super(username, password, authorities);
	}

	public CustomUser(EmployeeVO employeeVO) {
		//사용자가 정의한 (select한) MemberVO 타입의 객체 memberVO를
		//스프링 시큐리티에서 제공해주고 있는 UsersDetails 타입으로 변환
		//회원정보를 보내줄테니 이제부터 프링이 너가 관리해줘
		super(employeeVO.getEmpNo(),employeeVO.getEmpPwd(),
			employeeVO.getEmployeeAuthVOList().stream() //stream : 쭉 나열
			.map(auth->new SimpleGrantedAuthority(auth.getAuth())) //객체 2개가 생성됨
			.collect(Collectors.toList())
				);
		this.employeeVO = employeeVO;
	}

	public EmployeeVO getEmployeeVO() {
		return employeeVO;
	}

	public void setEmployeeVO(EmployeeVO employeeVO) {
		this.employeeVO = employeeVO;
	}
}

 

결과 화면7

 

 

더보기

테이블 정보

MEMBER 테이블

  

MEMBER_AUTH 테이블

 

 

데이터 삽입

INSERT INTO MEMBER_AUTH(USER_ID,AUTH)
SELECT USER_ID,'ROLE_MEMBER'
FROM MEMBER;

COMMIT;

SELECT * FROM MEMBER_AUTH;

 

결과 화면8

 

SELECT A.USER_ID, A.USER_NAME, A.PASSWORD, A.REG_DATE, A.COIN, A.ENABLED
    , B.AUTH
FROM MEMBER A, MEMBER_AUTH B
WHERE A.USER_ID = B.USER_ID
AND A.USER_ID = 'b001';

 

결과 화면9 : 암호화한 비밀번호로 PASSWORD에 넣어야 함

 

 

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="customAccessDenied" 
		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>
	
	<!-- 스프링 시큐리티의 UserDetailsService를 구현한 클래스를 빈으로 등록함 -->
	<bean id="customUserDetailsService2" class="kr.or.ddit.security.CustomUserDetailsService2">
	</bean>
	
	<security:http>
		<!-- URI 패턴으로 접근 제한을 설정함 -->
		<!-- URL을 가로챔 -->
		<!-- 누구나 접근 가능 -> 생략 가능 -->
		<security:intercept-url pattern="/board/list" access="permitAll"/> <!-- permitAll : 누구나 접근 가능 -->
		<security:intercept-url pattern="/board/register" access="hasRole('ROLE_MEMBER')" /> <!-- ROLE_MEMBER 권한을 가졌을 때 : 회원만 접근 -->
		<security:intercept-url pattern="/notice/list" access="permitAll" />
		<security:intercept-url pattern="/notice/register" access="hasRole('ROLE_ADMIN')" /> <!-- ROLE_ADMIN 권한을 가졌을 때 : 관리자만 접근 -->


		<!-- 사용자가 정의한 로그인 페이지의 URI를 지정함
		사용자 정의 로그인 성공 처리자를 지정함
		/login : get방식 -> LoginController에서 매핑 -->
		<security:form-login login-page="/login" 
			authentication-success-handler-ref="customLoginSuccess"/>

		<!-- 등록한 사용자 정의 bean을 접근 거부 처리자로 지정함 -->
		<security:access-denied-handler ref="customAccessDenied"/>
		
		<!-- 로그아웃 처리를 위한 URI를 지정하고, 로그아웃한 후에 세션을 무효화(session.invalidate())함 
		/logout : post방식 요청URI -> form의 action="/logout"에서 사용 -->
		<security:logout logout-url="/logout" invalidate-session="true" />
		
	</security:http>
	
	<security:authentication-manager>
		<!-- 지정된 아이디와 패스워드로 로그인이 가능하도록 설정함 -->
		<security:authentication-provider user-service-ref="customUserDetailsService2"> <!-- 인증 제공자 -->
			<!-- 비밀번호 암호화 -->
			<security:password-encoder ref="passwordEncoder"/>
		</security:authentication-provider>
		
	</security:authentication-manager>
</beans>

 

 

CustomUserDetailsService2.java

package kr.or.ddit.security;

import org.springframework.beans.factory.annotation.Autowired;
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.Controller;
import org.springframework.stereotype.Service;

import kr.or.ddit.mapper.MemberMapper;
import kr.or.ddit.vo.Member;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class CustomUserDetailsService2 implements UserDetailsService {

	@Autowired
	private MemberMapper memberMapper;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		log.info("CustomUserDetailsService2 -> username : " + username);
		
		//select
		Member member =  this.memberMapper.detail(username);
		log.info("CustomUserDetailsService2->member : " + member);
		
		//User(스프링꺼)
		//CustomUser2(우리꺼)
		return member==null?null:new CustomUser2(member);
	}

}

 

 

MemberMapper.java

root-context에 Mapper 인터페이스 설정을 미리 해둠

package kr.or.ddit.mapper;

import kr.or.ddit.vo.Member;

public interface MemberMapper {
	public Member detail(String username);
}

 

 

member2_SQL.xml

받아온 username의 값이 들어가는 위치

 

<?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.MemberMapper">

	<resultMap type="member" id="memberMap">
		<result property="userId" column="USER_ID"/>
		<result property="userName" column="USER_NAME"/>
		<result property="password" column="PASSWORD"/>
		<result property="regDate" column="REG_DATE"/>
		<result property="coin" column="COIN"/>
		<result property="enabled" column="ENABLED"/>
		<collection property="memberAuthVOList" resultMap="memberAuthMap"></collection>
	</resultMap>
	
	<resultMap type="memberAuthVO" id="memberAuthMap">
		<result property="userId" column="USER_ID"/>
		<result property="auth" column="AUTH"/>
	</resultMap>
	
	<!-- 부모(MEMBER) 1 : 자식(MEMBER_AUTH) N = 1 : N -->
	<select id="detail" parameterType="String" resultMap="memberMap">
		SELECT A.USER_ID, A.USER_NAME, A.PASSWORD, A.REG_DATE, A.COIN, A.ENABLED
		    , B.AUTH
		FROM MEMBER A, MEMBER_AUTH B
		WHERE A.USER_ID = B.USER_ID
		AND A.USER_ID = #{username}
	</select>
</mapper>

 

 

Member.java

package kr.or.ddit.vo;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.validation.Valid;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.multipart.MultipartFile;

import lombok.Data;

@Data
public class Member {
	@NotBlank
	private String userId;
	
	@NotBlank(message="사용자명을 입력해주세요.")
	@Size(max=3, message="최대 3글자까지만 작성해주세요.")
	private String userName;
	
	private String password = "1234";
	@DateTimeFormat(pattern="yyyy-MM-dd") private Date regDate;
	private int coin;
	private String enabled;
	
	//부모(MEMBER) 1 : 자식(MEMBER_AUTH) N = 1 : N
	private List<MemberAuthVO> memberAuthVOList;
}

 

 

MemberAuthVO.java

package kr.or.ddit.vo;

import lombok.Data;

@Data
public class MemberAuthVO {
	private String userId;
	private String auth;
}

 

 

mybatisAlias.xml
<typeAliases> 안에 추가

<typeAlias type="kr.or.ddit.vo.MemberAuthVO" alias="memberAuthVO"/>

 

 

CustomUser2.java

package kr.or.ddit.security;

import java.util.Collection;
import java.util.stream.Collectors;

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 kr.or.ddit.vo.Member;

//사용자가 유저를 정의함
//memberVO(select결과)정보를 User(스프링 시큐리티에서 정의된 유저) 객체 정보에 연계하여 넣어줌
//CustomUser의 객체 = principal
public class CustomUser2 extends User {

	private Member member;
	
	public CustomUser2(String username, String password, Collection<? extends GrantedAuthority> authorities) {
		super(username, password, authorities);
	}

	//return member==null?null:new CumstomUser2(member);
	public CustomUser2(Member member) {
		super(member.getUserId(), member.getPassword(), 
				member.getMemberAuthVOList().stream()
				.map(auth->new SimpleGrantedAuthority(auth.getAuth()))
				.collect(Collectors.toList())
				);
		
		this.member = member;
	}

	public Member getMember() {
		return member;
	}

	public void setMember(Member member) {
		this.member = member;
	}
}

 

결과 화면10

 

결과 화면11

 

 

 

  • 이름 변경 (principal)

aside.jsp

principal 추가 및 설정 관련 수정

<%@ 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"%>
<!-- Main Sidebar Container -->
<aside class="main-sidebar sidebar-dark-primary elevation-4">
	<!-- Brand Logo -->
	<a href="index3.html" class="brand-link"> <img
		src="/resources/adminlte/dist/img/AdminLTELogo.png"
		alt="AdminLTE Logo" class="brand-image img-circle elevation-3"
		style="opacity: .8"> <span class="brand-text font-weight-light">AdminLTE
			3</span>
	</a>

	<!-- Sidebar -->
	<div class="sidebar">
		<!-- Sidebar user panel (optional) -->
		<!-- /// 로그인 안함 시작 /// -->
		<!-- isAnonymous() 는 로그인 안 했을 때 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()">
		
			<!-- principal : 사용자정보 객체
			var속성이 있으면 JSTL변수에 할당.
			var속성이 없으면 화면에 출력.-->
			<sec:authentication property="principal.member" var="member"/>
			<sec:authentication property="principal.member.memberAuthVOList" var="memberAuthVOList" />
			
			<c:forEach var="memberAuthVO" items="${memberAuthVOList}" varStatus="stat">
				<p>${memberAuthVO.auth}</p>
			</c:forEach>
			
			<div class="user-panel mt-3 pb-3 mb-3 d-flex">
				<div class="image">
					<img src="/resources/upload/mang6.jpg"
						class="img-circle elevation-2" alt="User Image">
				</div>
				<div class="info">
					<a href="#" class="d-block">${member.userName}님 환영합니다.</a>
					<form action="/logout" method="post"> <!-- 꼭 post여야함 -->
						<button type="submit" class="btn btn-block btn-secondary btn-xs">로그아웃</button>
						<sec:csrfInput/>
					</form>
				</div>
			</div>
		</sec:authorize>
		<!-- /// 로그인 함 끝 /// -->


		<!-- SidebarSearch Form -->
		<div class="form-inline">
			<div class="input-group" data-widget="sidebar-search">
				<input class="form-control form-control-sidebar" type="search"
					placeholder="Search" aria-label="Search">
				<div class="input-group-append">
					<button class="btn btn-sidebar">
						<i class="fas fa-search fa-fw"></i>
					</button>
				</div>
			</div>
		</div>

		<!-- Sidebar Menu -->
		<nav class="mt-2">
			<ul class="nav nav-pills nav-sidebar flex-column"
				data-widget="treeview" role="menu" data-accordion="false">
				<!-- Add icons to the links using the .nav-icon class
               with font-awesome or any other icon font library -->
				<li class="nav-item menu-open"><a href="#"
					class="nav-link active"> <i
						class="nav-icon fas fa-tachometer-alt"></i>
						<p>
							Dashboard <i class="right fas fa-angle-left"></i>
						</p>
				</a>
					<ul class="nav nav-treeview">
						<li class="nav-item"><a href="./index.html"
							class="nav-link active"> <i class="far fa-circle nav-icon"></i>
								<p>Dashboard v1</p>
						</a></li>
						<li class="nav-item"><a href="./index2.html" class="nav-link">
								<i class="far fa-circle nav-icon"></i>
								<p>Dashboard v2</p>
						</a></li>
						<li class="nav-item"><a href="./index3.html" class="nav-link">
								<i class="far fa-circle nav-icon"></i>
								<p>Dashboard v3</p>
						</a></li>
					</ul></li>
				<li class="nav-item"><a href="pages/widgets.html"
					class="nav-link"> <i class="nav-icon fas fa-th"></i>
						<p>
							Widgets <span class="right badge badge-danger">New</span>
						</p>
				</a></li>
				<li class="nav-item"><a href="#" class="nav-link"> <i
						class="nav-icon fas fa-copy"></i>
						<p>
							Layout Options <i class="fas fa-angle-left right"></i> <span
								class="badge badge-info right">6</span>
						</p>
				</a>
					<ul class="nav nav-treeview">
						<li class="nav-item"><a href="pages/layout/top-nav.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Top Navigation</p>
						</a></li>
						<li class="nav-item"><a
							href="pages/layout/top-nav-sidebar.html" class="nav-link"> <i
								class="far fa-circle nav-icon"></i>
								<p>Top Navigation + Sidebar</p>
						</a></li>
						<li class="nav-item"><a href="pages/layout/boxed.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Boxed</p>
						</a></li>
						<li class="nav-item"><a
							href="pages/layout/fixed-sidebar.html" class="nav-link"> <i
								class="far fa-circle nav-icon"></i>
								<p>Fixed Sidebar</p>
						</a></li>
						<li class="nav-item"><a
							href="pages/layout/fixed-sidebar-custom.html" class="nav-link">
								<i class="far fa-circle nav-icon"></i>
								<p>
									Fixed Sidebar <small>+ Custom Area</small>
								</p>
						</a></li>
						<li class="nav-item"><a href="pages/layout/fixed-topnav.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Fixed Navbar</p>
						</a></li>
						<li class="nav-item"><a href="pages/layout/fixed-footer.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Fixed Footer</p>
						</a></li>
						<li class="nav-item"><a
							href="pages/layout/collapsed-sidebar.html" class="nav-link">
								<i class="far fa-circle nav-icon"></i>
								<p>Collapsed Sidebar</p>
						</a></li>
					</ul></li>
				<li class="nav-item"><a href="#" class="nav-link"> <i
						class="nav-icon fas fa-chart-pie"></i>
						<p>
							Charts <i class="right fas fa-angle-left"></i>
						</p>
				</a>
					<ul class="nav nav-treeview">
						<li class="nav-item"><a href="pages/charts/chartjs.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>ChartJS</p>
						</a></li>
						<li class="nav-item"><a href="pages/charts/flot.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Flot</p>
						</a></li>
						<li class="nav-item"><a href="pages/charts/inline.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Inline</p>
						</a></li>
						<li class="nav-item"><a href="pages/charts/uplot.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>uPlot</p>
						</a></li>
					</ul></li>
				<li class="nav-item"><a href="#" class="nav-link"> <i
						class="nav-icon fas fa-tree"></i>
						<p>
							UI Elements <i class="fas fa-angle-left right"></i>
						</p>
				</a>
					<ul class="nav nav-treeview">
						<li class="nav-item"><a href="pages/UI/general.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>General</p>
						</a></li>
						<li class="nav-item"><a href="pages/UI/icons.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Icons</p>
						</a></li>
						<li class="nav-item"><a href="pages/UI/buttons.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Buttons</p>
						</a></li>
						<li class="nav-item"><a href="pages/UI/sliders.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Sliders</p>
						</a></li>
						<li class="nav-item"><a href="pages/UI/modals.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Modals & Alerts</p>
						</a></li>
						<li class="nav-item"><a href="pages/UI/navbar.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Navbar & Tabs</p>
						</a></li>
						<li class="nav-item"><a href="pages/UI/timeline.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Timeline</p>
						</a></li>
						<li class="nav-item"><a href="pages/UI/ribbons.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Ribbons</p>
						</a></li>
					</ul></li>
				<li class="nav-item"><a href="#" class="nav-link"> <i
						class="nav-icon fas fa-edit"></i>
						<p>
							Forms <i class="fas fa-angle-left right"></i>
						</p>
				</a>
					<ul class="nav nav-treeview">
						<li class="nav-item"><a href="pages/forms/general.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>General Elements</p>
						</a></li>
						<li class="nav-item"><a href="pages/forms/advanced.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Advanced Elements</p>
						</a></li>
						<li class="nav-item"><a href="pages/forms/editors.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Editors</p>
						</a></li>
						<li class="nav-item"><a href="pages/forms/validation.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Validation</p>
						</a></li>
					</ul></li>
				<li class="nav-item"><a href="#" class="nav-link"> <i
						class="nav-icon fas fa-table"></i>
						<p>
							Tables <i class="fas fa-angle-left right"></i>
						</p>
				</a>
					<ul class="nav nav-treeview">
						<li class="nav-item"><a href="pages/tables/simple.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Simple Tables</p>
						</a></li>
						<li class="nav-item"><a href="pages/tables/data.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>DataTables</p>
						</a></li>
						<li class="nav-item"><a href="pages/tables/jsgrid.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>jsGrid</p>
						</a></li>
					</ul></li>
				<li class="nav-header">EXAMPLES</li>
				<li class="nav-item"><a href="pages/calendar.html"
					class="nav-link"> <i class="nav-icon far fa-calendar-alt"></i>
						<p>
							Calendar <span class="badge badge-info right">2</span>
						</p>
				</a></li>
				<li class="nav-item"><a href="pages/gallery.html"
					class="nav-link"> <i class="nav-icon far fa-image"></i>
						<p>Gallery</p>
				</a></li>
				<li class="nav-item"><a href="pages/kanban.html"
					class="nav-link"> <i class="nav-icon fas fa-columns"></i>
						<p>Kanban Board</p>
				</a></li>
				<li class="nav-item"><a href="#" class="nav-link"> <i
						class="nav-icon far fa-envelope"></i>
						<p>
							Mailbox <i class="fas fa-angle-left right"></i>
						</p>
				</a>
					<ul class="nav nav-treeview">
						<li class="nav-item"><a href="pages/mailbox/mailbox.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Inbox</p>
						</a></li>
						<li class="nav-item"><a href="pages/mailbox/compose.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Compose</p>
						</a></li>
						<li class="nav-item"><a href="pages/mailbox/read-mail.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Read</p>
						</a></li>
					</ul></li>
				<li class="nav-item"><a href="#" class="nav-link"> <i
						class="nav-icon fas fa-book"></i>
						<p>
							Pages <i class="fas fa-angle-left right"></i>
						</p>
				</a>
					<ul class="nav nav-treeview">
						<li class="nav-item"><a href="pages/examples/invoice.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Invoice</p>
						</a></li>
						<li class="nav-item"><a href="pages/examples/profile.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Profile</p>
						</a></li>
						<li class="nav-item"><a href="pages/examples/e-commerce.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>E-commerce</p>
						</a></li>
						<li class="nav-item"><a href="pages/examples/projects.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Projects</p>
						</a></li>
						<li class="nav-item"><a
							href="pages/examples/project-add.html" class="nav-link"> <i
								class="far fa-circle nav-icon"></i>
								<p>Project Add</p>
						</a></li>
						<li class="nav-item"><a
							href="pages/examples/project-edit.html" class="nav-link"> <i
								class="far fa-circle nav-icon"></i>
								<p>Project Edit</p>
						</a></li>
						<li class="nav-item"><a
							href="pages/examples/project-detail.html" class="nav-link"> <i
								class="far fa-circle nav-icon"></i>
								<p>Project Detail</p>
						</a></li>
						<li class="nav-item"><a href="pages/examples/contacts.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Contacts</p>
						</a></li>
						<li class="nav-item"><a href="pages/examples/faq.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>FAQ</p>
						</a></li>
						<li class="nav-item"><a href="pages/examples/contact-us.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Contact us</p>
						</a></li>
					</ul></li>
				<li class="nav-item"><a href="#" class="nav-link"> <i
						class="nav-icon far fa-plus-square"></i>
						<p>
							Extras <i class="fas fa-angle-left right"></i>
						</p>
				</a>
					<ul class="nav nav-treeview">
						<li class="nav-item"><a href="#" class="nav-link"> <i
								class="far fa-circle nav-icon"></i>
								<p>
									Login & Register v1 <i class="fas fa-angle-left right"></i>
								</p>
						</a>
							<ul class="nav nav-treeview">
								<li class="nav-item"><a href="pages/examples/login.html"
									class="nav-link"> <i class="far fa-circle nav-icon"></i>
										<p>Login v1</p>
								</a></li>
								<li class="nav-item"><a href="pages/examples/register.html"
									class="nav-link"> <i class="far fa-circle nav-icon"></i>
										<p>Register v1</p>
								</a></li>
								<li class="nav-item"><a
									href="pages/examples/forgot-password.html" class="nav-link">
										<i class="far fa-circle nav-icon"></i>
										<p>Forgot Password v1</p>
								</a></li>
								<li class="nav-item"><a
									href="pages/examples/recover-password.html" class="nav-link">
										<i class="far fa-circle nav-icon"></i>
										<p>Recover Password v1</p>
								</a></li>
							</ul></li>
						<li class="nav-item"><a href="#" class="nav-link"> <i
								class="far fa-circle nav-icon"></i>
								<p>
									Login & Register v2 <i class="fas fa-angle-left right"></i>
								</p>
						</a>
							<ul class="nav nav-treeview">
								<li class="nav-item"><a href="pages/examples/login-v2.html"
									class="nav-link"> <i class="far fa-circle nav-icon"></i>
										<p>Login v2</p>
								</a></li>
								<li class="nav-item"><a
									href="pages/examples/register-v2.html" class="nav-link"> <i
										class="far fa-circle nav-icon"></i>
										<p>Register v2</p>
								</a></li>
								<li class="nav-item"><a
									href="pages/examples/forgot-password-v2.html" class="nav-link">
										<i class="far fa-circle nav-icon"></i>
										<p>Forgot Password v2</p>
								</a></li>
								<li class="nav-item"><a
									href="pages/examples/recover-password-v2.html" class="nav-link">
										<i class="far fa-circle nav-icon"></i>
										<p>Recover Password v2</p>
								</a></li>
							</ul></li>
						<li class="nav-item"><a href="pages/examples/lockscreen.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Lockscreen</p>
						</a></li>
						<li class="nav-item"><a
							href="pages/examples/legacy-user-menu.html" class="nav-link">
								<i class="far fa-circle nav-icon"></i>
								<p>Legacy User Menu</p>
						</a></li>
						<li class="nav-item"><a
							href="pages/examples/language-menu.html" class="nav-link"> <i
								class="far fa-circle nav-icon"></i>
								<p>Language Menu</p>
						</a></li>
						<li class="nav-item"><a href="pages/examples/404.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Error 404</p>
						</a></li>
						<li class="nav-item"><a href="pages/examples/500.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Error 500</p>
						</a></li>
						<li class="nav-item"><a href="pages/examples/pace.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Pace</p>
						</a></li>
						<li class="nav-item"><a href="pages/examples/blank.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Blank Page</p>
						</a></li>
						<li class="nav-item"><a href="starter.html" class="nav-link">
								<i class="far fa-circle nav-icon"></i>
								<p>Starter Page</p>
						</a></li>
					</ul></li>
				<li class="nav-item"><a href="#" class="nav-link"> <i
						class="nav-icon fas fa-search"></i>
						<p>
							Search <i class="fas fa-angle-left right"></i>
						</p>
				</a>
					<ul class="nav nav-treeview">
						<li class="nav-item"><a href="pages/search/simple.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Simple Search</p>
						</a></li>
						<li class="nav-item"><a href="pages/search/enhanced.html"
							class="nav-link"> <i class="far fa-circle nav-icon"></i>
								<p>Enhanced</p>
						</a></li>
					</ul></li>
				<li class="nav-header">MISCELLANEOUS</li>
				<li class="nav-item"><a href="iframe.html" class="nav-link">
						<i class="nav-icon fas fa-ellipsis-h"></i>
						<p>Tabbed IFrame Plugin</p>
				</a></li>
				<li class="nav-item"><a href="https://adminlte.io/docs/3.1/"
					class="nav-link"> <i class="nav-icon fas fa-file"></i>
						<p>Documentation</p>
				</a></li>
				<li class="nav-header">MULTI LEVEL EXAMPLE</li>
				<li class="nav-item"><a href="#" class="nav-link"> <i
						class="fas fa-circle nav-icon"></i>
						<p>Level 1</p>
				</a></li>
				<li class="nav-item"><a href="#" class="nav-link"> <i
						class="nav-icon fas fa-circle"></i>
						<p>
							Level 1 <i class="right fas fa-angle-left"></i>
						</p>
				</a>
					<ul class="nav nav-treeview">
						<li class="nav-item"><a href="#" class="nav-link"> <i
								class="far fa-circle nav-icon"></i>
								<p>Level 2</p>
						</a></li>
						<li class="nav-item"><a href="#" class="nav-link"> <i
								class="far fa-circle nav-icon"></i>
								<p>
									Level 2 <i class="right fas fa-angle-left"></i>
								</p>
						</a>
							<ul class="nav nav-treeview">
								<li class="nav-item"><a href="#" class="nav-link"> <i
										class="far fa-dot-circle nav-icon"></i>
										<p>Level 3</p>
								</a></li>
								<li class="nav-item"><a href="#" class="nav-link"> <i
										class="far fa-dot-circle nav-icon"></i>
										<p>Level 3</p>
								</a></li>
								<li class="nav-item"><a href="#" class="nav-link"> <i
										class="far fa-dot-circle nav-icon"></i>
										<p>Level 3</p>
								</a></li>
							</ul></li>
						<li class="nav-item"><a href="#" class="nav-link"> <i
								class="far fa-circle nav-icon"></i>
								<p>Level 2</p>
						</a></li>
					</ul></li>
				<li class="nav-item"><a href="#" class="nav-link"> <i
						class="fas fa-circle nav-icon"></i>
						<p>Level 1</p>
				</a></li>
				<li class="nav-header">LABELS</li>
				<li class="nav-item"><a href="#" class="nav-link"> <i
						class="nav-icon far fa-circle text-danger"></i>
						<p class="text">Important</p>
				</a></li>
				<li class="nav-item"><a href="#" class="nav-link"> <i
						class="nav-icon far fa-circle text-warning"></i>
						<p>Warning</p>
				</a></li>
				<li class="nav-item"><a href="#" class="nav-link"> <i
						class="nav-icon far fa-circle text-info"></i>
						<p>Informational</p>
				</a></li>
			</ul>
		</nav>
		<!-- /.sidebar-menu -->
	</div>
	<!-- /.sidebar -->
</aside>

 

결과 화면12

 

 

반응형
반응형
스프링 시큐리티

 

: 애플리케이션에서 보안 기능을 구현하는 데 사용되는 프레임 워크

 

 

 

  • 기본 보안 기능

- 인증

: 사용자의 정당성 확인

ex) 로그인

 

- 인가

: 리소스나 처리에 대한 접근 제어

ex) 권한

 

 

 

설정

 

 

pom.xml

</dependencies> 안에 추가 (의존 라이브러리 4개 추가)

		<!-- 스프링 시큐리티 설정을 도와줌 -->
		<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->
		<dependency>
		    <groupId>org.springframework.security</groupId>
		    <artifactId>spring-security-config</artifactId>
		    <version>5.0.7.RELEASE</version>
		</dependency>
		
		<!-- 스프링 시큐리티 일반 기능 -->
		<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->
		<dependency>
		    <groupId>org.springframework.security</groupId>
		    <artifactId>spring-security-core</artifactId>
		    <version>5.0.7.RELEASE</version>
		</dependency>
		
		<!-- 스프링 시큐리티와 태그라이브러리를 연결해줌 -->
		<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-taglibs -->
		<dependency>
		    <groupId>org.springframework.security</groupId>
		    <artifactId>spring-security-taglibs</artifactId>
		    <version>5.0.7.RELEASE</version>
		</dependency>
		
		<!-- 스프링 시큐리티 라이브러리 의존관계 정의 시작 -->

 

 

web.xml

root-context.xml의 </context-param> 안을 변경 (스프링 시큐리티 설정 파일 지정 후)

	<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
	<!-- contextConfigLocation에 스프링 시큐리티 설정 파일을 지정 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring/root-context.xml
			/WEB-INF/spring/security-context.xml
		</param-value>
	</context-param>

 

</web-app> 안에 추가 (필터 낌)

	<!-- 스프링 시큐리티가 제공하는 서블릿 필터 클래스를 서블릿 컨테이너에 등록함 -->
	<!-- filter과  filter-mapping의 filter-name은 서로 같아야 함-->
	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>springSecurityFilterChain</filter-name>
		<url-pattern>/*</url-pattern> <!-- 모든 요청에서 필터를 가동시킴 -->
	</filter-mapping>

 

 

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

	<security:http>
		<!-- 폼 기반 인증 기능을 사용 -->
		<security:form-login/>
	</security:http>
	
	<!-- authentication : 인증(로그인) -->
	<security:authentication-manager>
	</security:authentication-manager>
</beans>

 

=> 재기동 시 콘솔 창에서 에러나지 않으면 문제 없음

 

 

 

실습

 

  • 웹화면 접근 정책

 

 

BoardController.java

package kr.or.ddit.controller;

import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import kr.or.ddit.dao.BoardDao;
import kr.or.ddit.vo.BoardVO;
import lombok.extern.slf4j.Slf4j;


@Slf4j
@Controller
@RequestMapping("/board")
public class BoardController {
	@RequestMapping(value="/register", method=RequestMethod.GET)
	public String registerForm() {
		log.info("registerForm에 왔다");
		
		//ModelAndView가 없음
		//mav.setViewname("board/register") 생략
		
		//forwarding : jsp
		return "board/register";
	}

	@RequestMapping("/list")
	public String list() {
		log.info("list에 왔다");
		
		// forwarding : /views/board/list.jsp
		return "board/list";
	}
}

 


list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<h2>누구나 접근 가능</h2>
<h3>/board/list.jsp</h3>

 


register.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<h2>로그인 한 회원만 접근 가능</h2>
<h3>board/register.jsp</h3>

 

 

NoticeController.java

package kr.or.ddit.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import lombok.extern.slf4j.Slf4j;

@RequestMapping("/notice")
@Slf4j
@Controller
public class NoticeController {

	//요청 URI : /notice/list
	@GetMapping("/list")
	public String list() {
		//forwarding : jsp
		return "notice/list";
	}
	
	//요청 URI : /notice/register
	@GetMapping("/register")
	public String register() {
		//forwarding : jsp
		return "notice/register";
	}
}

 

 

notice/list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<h2>누구나 접근 가능</h2>
<h3>/notice/list.jsp</h3>

 

 

notice/register.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<h2>로그인 한 관리자만 접근 가능</h2>
<h3>/notice/register.jsp</h3>

 

결과 화면1

 

결과 화면2

 

결과 화면3

 

결과 화면4

 

 

 

접근 제한 설정

 

 

security-context.xml

<security:http> 안에 추가

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

 

결과 화면5 : localhost/notice/register URL 입력 시 화면

 

 

 

로그인 처리

 

 

스프링 시큐리니티 5부터 기본적으로 PasswordEncoder를 지정해야 하는데, 그 이유는 사용자 테이블(USERS)에 비밀번호를 암호화하여 저장해야함.


우리는 우선 비밀번호를 암호화 처리 하지 않았으므로 암호화 하지 않는 PasswordEncoder를 직접 구현하여 지정하기로 함
noop : no option password

 

 

security-context.xml
</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>

 

결과 화면6-1 : localhost/notice/register ❘ member와 admin으로 로그인 가능

 

결과 화면6-2

 

=> 출력 결과

/board/register : admin 접근 o, member 접근 o
/notice/register : admin 접근 o, member 접근 x

 

 

 

접근 거부 처리

 

 

security-context.xml
</security:http> 안에 추가

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

 

 

SecurityController.java

package kr.or.ddit.controller;

import org.apache.catalina.authenticator.SpnegoAuthenticator.AuthenticateAction;
import org.springframework.security.core.Authentication;
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 SecurityController {
	
	//인증(Authentication) 거부
	//요청URI : /accessError
	@GetMapping("/accessError")
	public String accessError(Authentication auth, Model model) {
		//인증과 관련된 정보를 확인
		log.info("access Denied : " + auth);
		
		model.addAttribute("msg", "Access Denied");
		
		//forwarding : jsp
		return "accessError";
	}
}

 

 

accessError.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<h3>Access Denied</h3>
<!-- 403 오류가 났을 때 자동으로 발생 후 메시지 출력해줌 (자동) -->
<h2>${SPRING_SECURITY_403_EXCEPTION.getMessage()}</h2>
<h2>${msg}</h2>

 

결과 화면7-1 : localhost/notice/register 의 권한이 없는 member 로 로그인 진행

 

결과 화면7-2

 

 

 

사용자 정의 접근 거부 처리자

 

 

security-context.xml
</security:http> 안에 추가 및 접근 거부 처리자의 URI를 지정 주석처리

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

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


</beans> 안에 추가

	<!-- CustomAccessDeniedHandler customAccessDenied = new CustomAccessDeniedHandler(); 와 같은 문장임 -->
	<bean id="customAccessDenied" 
		class="kr.or.ddit.security.CustomAccessDeniedHandler"></bean>

 

 

CustomAccessDeniedHandler.java

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작업
		//로그작업
		//메시지발송
		
		//redirect : 새로운 URI를 요청
		response.sendRedirect("/accessError");
	}
	
	/*
	공지사항 등록 화면(/notice/register)은 
	일반회원(member/java)이 접근할 수 없는 페이지이고,
	관리자(admin/java)만 접근 가능하므로..
	지정된 접근 거부 처리자(CustomAccessDeniedHander)에서 
	접근 거부 처리 페이지(/accessError)로 리다이렉트 시킴
	*/
	
}

 

결과 화면8 : localhost/notice/register ❘ 아이디 : member 비밀번호 : 1234로 로그인 시 화면

 

결과 화면9 : 접근 거부 처리 컨트롤러로 갔다는 것을 확인할 수 있음

 

 

 

사용자 정의 로그인 페이지

 

 

security-context.xml

</security:http> 안에 수정 및 추가

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

<!-- 사용자가 정의한 로그인 페이지의 URI를 지정함 -->
<security:form-login login-page="/login"/>

 

 

LoginController.java

package kr.or.ddit.controller;

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

@Controller
public class LoginController {
	
	@GetMapping("/login")
	public String loginForm() {
		//forwarding : jsp
		// /views/ + loginForm + ".jsp";
		return "loginForm";
	}
}

 

 

loginForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<div class="login-box" style="margin:auto;">
	<div class="card">
		<div class="card-body login-card-body">
			<p class="login-box-msg">Sign in to start your session</p>
			<form action="../../index3.html" method="post">
				<div class="input-group mb-3">
					<input type="email" class="form-control" placeholder="Email">
					<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" class="form-control" placeholder="Password">
					<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>
			</form>
		</div>
	</div>
</div>

 

결과 화면10

 

 

 

  • 기능 추가

 

필수로 설정해야 하는 것

 

 

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

<div class="login-box" style="margin:auto;">
	<div class="card">
		<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">
					<!-- 아이디 
					name="username" 으로 꼭 써야함 -->
					<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">
				<!-- 비밀번호
				name="password" 으로 꼭 써야함 -->
					<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: 를 사용하기 위해선 상단에 추가해야함
				taglib prefix="sec" uri="http://www.springframework.org/security/tags -->
				<sec:csrfInput/>
			</form>
		</div>
	</div>
</div>

 

결과 화면11 : http://localhost/notice/register 로 들어올 시 자동으로 이동됨

 

 

반응형
1 2 3 4 5 ··· 11