드디어 스프링 security를 배웠다.
프로젝트 마무리한다고 따라가기 벅차지만 어차피 프로젝트 완성할 때 적용해야되니까
그냥 매 먼저 맞고 있다..
버전 올릴 것 올리고, 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>
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: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>
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() 호출");
}
}
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표현식으로 출력됨
위의 전체 코드 참고
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
}
}
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);
}
}
<%@ 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>```