스프링 시큐리티
: 애플리케이션에서 보안 기능을 구현하는 데 사용되는 프레임 워크
- 기본 보안 기능
- 인증
: 사용자의 정당성 확인
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>
접근 제한 설정
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부터 기본적으로 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>
=> 출력 결과
/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>
사용자 정의 접근 거부 처리자
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)로 리다이렉트 시킴
*/
}
사용자 정의 로그인 페이지
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>
- 기능 추가
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>
'스프링 프레임워크' 카테고리의 다른 글
[스프링 프레임워크] 29장 자동 로그인, 스프링 시큐리티 애너테이션 (1) | 2024.05.20 |
---|---|
[스프링 프레임워크] 28장 스프링 시큐리티2 (0) | 2024.05.17 |
[스프링 프레임워크] 26장 트랙잭션, 예외처리 (0) | 2024.05.16 |
[스프링 프레임워크] 25장 AOP (0) | 2024.05.16 |
[스프링 프레임워크] 24장 페이징 처리 (0) | 2024.05.14 |