[spring] spring security

DEVRANG·2022년 11월 16일
0
post-thumbnail

드디어 스프링 security를 배웠다.
프로젝트 마무리한다고 따라가기 벅차지만 어차피 프로젝트 완성할 때 적용해야되니까
그냥 매 먼저 맞고 있다..

준비

pom.xml

버전 올릴 것 올리고, security 라이브러리 설치해줘야함 (core, web, config, taglibs)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.itwillbs</groupId>
	<artifactId>web</artifactId>
	<name>SpringSecurity</name>
	<packaging>war</packaging>
	<version>1.0.0-BUILD-SNAPSHOT</version>
	<properties>
		<java-version>1.8</java-version>
		<org.springframework-version>5.0.6.RELEASE</org.springframework-version>
		<org.aspectj-version>1.6.10</org.aspectj-version>
		<org.slf4j-version>1.6.6</org.slf4j-version>
	</properties>
	<dependencies>
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework-version}</version>
			<exclusions>
				<!-- Exclude Commons Logging in favor of SLF4j -->
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				 </exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
				
		<!-- AspectJ -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>	
		
		<!-- Logging -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${org.slf4j-version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.15</version>
			<exclusions>
				<exclusion>
					<groupId>javax.mail</groupId>
					<artifactId>mail</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.jms</groupId>
					<artifactId>jms</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jdmk</groupId>
					<artifactId>jmxtools</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jmx</groupId>
					<artifactId>jmxri</artifactId>
				</exclusion>
			</exclusions>
			<scope>runtime</scope>
		</dependency>

		<!-- @Inject -->
		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
		</dependency>
				
		<!-- Servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
	
		<!-- Test -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency> 
		
		
		<!-- 외부 라이브러리 -->
		<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.30</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.4.1</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>1.3.0</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>


		<!-- https://mvnrepository.com/artifact/org.bgee.log4jdbc-log4j2/log4jdbc-log4j2-jdbc4.1 -->
		<dependency>
			<groupId>org.bgee.log4jdbc-log4j2</groupId>
			<artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
			<version>1.16</version>
		</dependency>


		<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.24</version>
			<scope>provided</scope>
		</dependency>


//여기가 security라이브러리 추가 부분!!! 

		<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-core</artifactId>
			<version>5.0.6.RELEASE</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>5.0.6.RELEASE</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>5.0.6.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.6.RELEASE</version>
		</dependency>




		<!-- 외부 라이브러리 -->
		
		       
	</dependencies>
	
	
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-eclipse-plugin</artifactId>
                <version>2.9</version>
                <configuration>
                    <additionalProjectnatures>
                        <projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
                    </additionalProjectnatures>
                    <additionalBuildcommands>
                        <buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
                    </additionalBuildcommands>
                    <downloadSources>true</downloadSources>
                    <downloadJavadocs>true</downloadJavadocs>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <compilerArgument>-Xlint:all</compilerArgument>
                    <showWarnings>true</showWarnings>
                    <showDeprecation>true</showDeprecation>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                    <mainClass>org.test.int1.Main</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

web.xml

security-context.xml파일도 <param-value>에 추가하고, 시큐리티 필터 추가+매핑
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

	<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
        //security-context.xml도 추가해줌!! 
		<param-value>
			/WEB-INF/spring/root-context.xml 
			/WEB-INF/spring/security-context.xml
		</param-value>
	</context-param>
	
	<!-- Creates the Spring Container shared by all Servlets and Filters -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
		
	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
    //시큐리티 필터 설정 한 뒤 매핑 
		<!-- 스프링시큐리티 필터 설정 -->
	<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>

</web-app>

security-context.xml

<security:intercept-url : pattern:controller의 
 =>가상주소가 들어오면 access실행됨

<security:intercept-url pattern="/admin/*" 
						access="hasRole('ROLE_ADMIN')"/>
 => 이런식으로 "/admin/*"으로 들어오는 모든 주소로 pattern 정해두면,
 	admin controller에 있는 모든 주소 들어왔을 때 자동으로 login check할 수 있게 설정 가능함(보다 안전)
    

<!-- 인증매니저  -->
<security:authentication-manager>
	<security:authentication-provider>
		<security:user-service>
			<security:user name="itwill" password="{noop}1234" authorities="ROLE_MEMBER" />
            //itwill아이디에게 member의 권한 부여 
			<security:user name="admin" password="{noop}1234" authorities="ROLE_MEMBER,ROLE_ADMIN" />
            //admin아이디에게 member권한,admin권한 부여 
		</security:user-service>
	</security:authentication-provider>
</security:authentication-manager>
<!-- password {noop}: 지금 당장은 비밀번호 인코딩을 안하겠다는 의미, 인코딩없이 처리가능하도록 만들어줌   -->
<!-- itwill아이디의 허용되는 범위는 member까지 : all,member페이지 접속 가능(권한 ROLE을 2개줌), admin페이지 접속 불가능  -->
<!-- admin아이디는 관리자 권한: all,member,admin모두 접속 가능  -->
//예외처리
<security:access-denied-handler error-page="/accessError"/>
	//=> 이렇게 페이지 호출 가상주소 매번 부르면 불편.. 
<security:access-denied-handler ref="customAccessDenied"/>
	//=>customAccessDenied이라는 AccessDenied인터페이스 상속 class만들어서 
    	생성된 객체에 접근해서 예외(오류)처리 메서드 실행함
<!-- 에러가 발생하면 에러 시 가상주소를 바로 부르는 것이 아니라, 
customAccessDined의 객체에 접근해서 오류처리 메서드 실행함 -->

<전체코드>

<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
	
	<!-- 접근권한 처리객체 생성 : customAccessDenied의 객체 생성해서 오버라이딩한 handeler메서드 사용하기 위해-->
	<bean id="customAccessDenied"
		  class="com.itwilbs.security.CustomAccessDeniedHandeler"
		  />
	
	<security:http>
		<security:intercept-url pattern="/sample/all" access="permitAll"/>
		<security:intercept-url pattern="/sample/member" 
								access="hasRole('ROLE_MEMBER')"/>
		<security:intercept-url pattern="/sample/admin" 
								access="hasRole('ROLE_ADMIN')"/>
								
		<security:intercept-url pattern="/sample/*" 
								access="hasRole('ROLE_ADMIN')"/>
	
		<security:form-login/>
        //시큐리티 적용되어 위의 적용되는 url들어오면 로그인 폼 만들어져서 로그인 해야 페이지 접근 가능 
		
<!-- 		<security:access-denied-handler error-page="/accessError"/>
 -->		<security:access-denied-handler ref="customAccessDenied"/>
 			<!-- 에러가 발생하면 에러 시 가상주소를 바로 부르는 것이 아니라, customAccessDined의 객체에 접근해서 오류처리 메서드 실행함 -->
		
		
	</security:http>
	<!-- <security:access-denied-handler/> :예외처리 해주는 태그(오류발생 시 이동할 페이지) -->
	
	<!-- 인증매니저  -->
	<security:authentication-manager>
		<security:authentication-provider>
			<security:user-service>
				<security:user name="itwill" 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>
	<!-- password {noop}: 지금 당장은 비밀번호 인코딩을 안하겠다는 의미, 인코딩없이 처리가능하도록 만들어줌   -->
	<!-- itwill아이디의 허용되는 범위는 member까지 : all,member페이지 접속 가능(권한 ROLE을 2개줌), admin페이지 접속 불가능  -->
	<!-- admin아이디는 관리자 권한: all,member,admin모두 접속 가능  -->

</beans>

SampleController.java

package com.itwillbs.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/sample/*")
public class SampleController {
	

	private static final Logger log 
						= LoggerFactory.getLogger(SampleController.class);
	
	//  /sample/all 주소 호출 시 -> 로그인 없이 페이지 접속 
	//@RequestMapping(value = "/all",method = RequestMethod.GET) 이것과 의미 동일
	@GetMapping(value = "/all")
	public void allGet() {
		log.info("allGet() 호출");
	}
	
	//  /sample/member -> 로그인 한 사람만 페이지 접속
	@GetMapping(value = "/member")
	public void memberGet() {
		log.info("memberGet() 호출");
	}
	
	//  /sample/admin -> 로그인+관리자인 경우 별도 구분 
	@GetMapping(value = "/admin")
	public void adminGet() {
		log.info("adminGet() 호출");
	}
}

view페이지

all.jsp (로그인X,모든사람)
member.jsp(로그인O,회원)
admin.jsp(로그인O,관리자)

예외처리

1.security-context.xml에서 <security:access-denied-handler/> 
  예외처리 태그 ref값을 접근할 객체의 bean id로 줌 
  
2. CustomAccessDeniedHandeler.java 핸들러 객체 생성하여 예외 발생 시, 
  예외처리하는 핸들러 생성하여 예외처리 시 호출되는 페이지 호출하는 메서드 생성 (여기서 예외처리 뷰 페이지 호출 가상주소 불러옴)
  
3. 일반 컨트롤러(commonController.java)에 예외 처리 시 보여지는 뷰페이지 호출 가상주소 메서드가 있고,
   model.addAttribute해서 값을 담아 뷰페이지로 보낼 수 있음 
   
4. 보여지는 뷰페이지(accessError.jsp)에서 일반 컨트롤러에서 보낸 값 el표현식으로 출력됨

security-context.xml

위의 전체 코드 참고

CustomAccessDeniedHandeler.java

package com.itwilbs.security;

import java.io.IOException;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;

public class CustomAccessDeniedHandeler implements AccessDeniedHandler{
	//Spring에서 제공하는 AccessDeniedHandler 구현 

	private static final Logger log 
						= LoggerFactory.getLogger(CustomAccessDeniedHandeler.class);
	//인터페이스 메서드 오버라이딩 
	@Override
	public void handle(HttpServletRequest request, HttpServletResponse response,
			AccessDeniedException accessDeniedException) throws IOException, ServletException {
		
		log.info("CustomAccessDeniedHandler - handler()호출");
		
		log.info("권한없이 접근중....페이지 이동");
		
		response.sendRedirect("/accessError"); //페이지 이동->accessError.jsp
		
	}
}

CommonController.java

package com.itwillbs.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class CommonController {
	

	private static final Logger log 
							= LoggerFactory.getLogger(CommonController.class);
	
	//에러페이지로 값 보내는 메서드 
	@GetMapping(value = "/accessError")
	public void accessDenied(Model model,Authentication auth) {
		log.info("accessDenied()호출");
		log.info("접근권한이 없음");
		
		model.addAttribute("msg", "접근권한 없는 사용!");
		model.addAttribute("auth", auth);
	}
}

accessError.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>accessError.jsp 접근권한이 없음</h1>
	<h2>관리자에게 문의하세요.</h2>
	
	msg: ${msg }
	<hr>
	auth: ${auth }
	<hr>
	${SPRING_SECURITY_403_EXCEPTION.getMessage() }
</body>
</html>```




profile
완주가 목표인 호랑이

0개의 댓글