웹사이트 검색

스프링 시큐리티 예제 튜토리얼


Spring Security는 웹 애플리케이션에서 인증 및 권한 부여를 수행하는 방법을 제공합니다. 모든 서블릿 기반 웹 애플리케이션에서 스프링 보안을 사용할 수 있습니다.

스프링 시큐리티

  1. 검증된 기술, 바퀴를 재발명하는 것보다 이것을 사용하는 것이 낫습니다. 보안은 특별한 주의가 필요한 부분입니다. 그렇지 않으면 애플리케이션이 공격자에게 취약해집니다.
  2. CSRF, 세션 고정 공격과 같은 일반적인 공격을 방지합니다.
  3. 모든 웹 애플리케이션에 쉽게 통합할 수 있습니다. 우리는 웹 애플리케이션 구성을 수정할 필요가 없으며 Spring은 자동으로 웹 애플리케이션에 보안 필터를 주입합니다.
  4. 메모리 내, DAO, JDBC, LDAP 등 다양한 방식으로 인증 지원을 제공합니다.
  5. 정적 HTML, 이미지 파일을 제공하는 데 적합한 특정 URL 패턴을 무시하는 옵션을 제공합니다.
  6. 그룹 및 역할 지원

스프링 시큐리티 예제

  1. 메모리 내
  2. 다오
  3. JDBC

JDBC의 경우 MySQL 데이터베이스를 사용하고 있으며 사용자 세부 정보 테이블을 생성하기 위해 다음 스크립트를 실행했습니다.

CREATE TABLE `Employees` (
  `username` varchar(20) NOT NULL DEFAULT '',
  `password` varchar(20) NOT NULL DEFAULT '',
  `enabled` tinyint(1) NOT NULL DEFAULT '1',
  PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `Roles` (
  `username` varchar(20) NOT NULL DEFAULT '',
  `role` varchar(20) NOT NULL DEFAULT '',
  PRIMARY KEY (`username`,`role`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `Employees` (`username`, `password`, `enabled`)
VALUES
	('pankaj', 'pankaj123', 1);

INSERT INTO `Roles` (`username`, `role`)
VALUES
	('pankaj', 'Admin'),
	('pankaj', 'CEO');

commit;

또한 서블릿 컨테이너에서 JDBC DataSource를 JNDI로 구성해야 합니다. 이에 대해 알아보려면 Tomcat JNDI DataSource 예제를 읽어보십시오.

스프링 시큐리티 메이븐 의존성

다음은 최종 pom.xml 파일입니다.

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>WebappSpringSecurity</groupId>
	<artifactId>WebappSpringSecurity</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<dependencies>
		<!-- Spring Security Artifacts - START -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>3.2.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>3.2.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-taglibs</artifactId>
			<version>3.0.5.RELEASE</version>
		</dependency>
		<!-- Spring Security Artifacts - END -->

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
			<scope>compile</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>javax.servlet-api</artifactId>
			<version>3.0.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.1.1</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>4.0.2.RELEASE</version>
		</dependency>
	</dependencies>
	<build>
		<sourceDirectory>src</sourceDirectory>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>1.7</source>
					<target>1.7</target>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.3</version>
				<configuration>
					<warSourceDirectory>WebContent</warSourceDirectory>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Spring Framework와 관련된 의존성은 다음과 같습니다.

  1. spring-jdbc: JDBC 인증 방식에 의한 JDBC 동작에 사용된다. JNDI로 DataSource 설정이 필요합니다. 사용법에 대한 전체 예제는 Spring DataSource JNDI 예제를 참조하세요.
  2. spring-security-taglibs: Spring Security 태그 라이브러리, JSP 페이지에서 사용자 역할을 표시하는 데 사용했습니다. 대부분의 경우 필요하지 않습니다.
  3. spring-security-config: JDBC, DAO, LDAP 등을 사용할지 여부와 같은 인증 공급자를 구성하는 데 사용됩니다.
  4. spring-security-web: 이 구성 요소는 Spring Security를 Servlet API에 통합합니다. 웹 애플리케이션에서 보안 구성을 플러그인하려면 필요합니다.

또한 Servlet API 3.0 기능을 사용하여 프로그래밍 방식으로 리스너와 필터를 추가할 것이므로 종속 항목의 servlet api 버전이 3.0 이상이어야 합니다.

Spring Security 예제 보기 페이지

애플리케이션에 JSP 및 HTML 페이지가 있습니다. HTML 페이지를 제외한 모든 페이지에 인증을 적용하고자 합니다. 건강.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Health Check</title>
</head>
<body>
    <h3>Service is up and running!!</h3>
</body>
</html>

<코드>index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="https://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="https://www.springframework.org/security/tags" prefix="sec" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Home Page</title>
</head>
<body>
<h3>Home Page</h3>

	<p>
      Hello <b><c:out value="${pageContext.request.remoteUser}"/></b><br>
      Roles: <b><sec:authentication property="principal.authorities" /></b>
    </p>
    
    <form action="logout" method="post">
      <input type="submit" value="Logout" />
      <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
    </form>
</body>
</html>

응용 프로그램 배포 설명자에 welcome-fileindex.jsp를 포함했습니다. Spring Security는 CSRF 공격을 처리하므로 로그아웃을 위해 양식을 제출할 때 CSRF 토큰을 서버로 다시 전송하여 삭제합니다. Spring Security 구성 요소에 의해 설정된 CSRF 개체는 _csrf이며 로그아웃 요청에서 전달하기 위해 속성 이름과 토큰 값을 사용하고 있습니다. 이제 Spring Security 구성을 살펴보겠습니다.

스프링 보안 예제 UserDetailsService DAO 구현

DAO 기반 인증도 사용할 것이므로 UserDetailsService 인터페이스를 구현하고 loadUserByUsername() 메서드에 대한 구현을 제공해야 합니다. 이상적으로는 일부 리소스를 사용하여 사용자를 확인해야 하지만 간단하게 하기 위해 기본 확인만 수행합니다. AppUserDetailsServiceDAO.java

package com.journaldev.webapp.spring.dao;

import java.util.Collection;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

public class AppUserDetailsServiceDAO implements UserDetailsService {

	protected final Log logger = LogFactory.getLog(getClass());
	
	@Override
	public UserDetails loadUserByUsername(final String username)
			throws UsernameNotFoundException {
		
		logger.info("loadUserByUsername username="+username);
		
		if(!username.equals("pankaj")){
			throw new UsernameNotFoundException(username + " not found");
		}
		
		//creating dummy user details, should do JDBC operations
		return new UserDetails() {
			
			private static final long serialVersionUID = 2059202961588104658L;

			@Override
			public boolean isEnabled() {
				return true;
			}
			
			@Override
			public boolean isCredentialsNonExpired() {
				return true;
			}
			
			@Override
			public boolean isAccountNonLocked() {
				return true;
			}
			
			@Override
			public boolean isAccountNonExpired() {
				return true;
			}
			
			@Override
			public String getUsername() {
				return username;
			}
			
			@Override
			public String getPassword() {
				return "pankaj123";
			}
			
			@Override
			public Collection<? extends GrantedAuthority> getAuthorities() {
				List<SimpleGrantedAuthority> auths = new java.util.ArrayList<SimpleGrantedAuthority>();
				auths.add(new SimpleGrantedAuthority("admin"));
				return auths;
			}
		};
	}

}

UserDetails의 익명 내부 클래스를 생성하고 이를 반환한다는 점에 유의하십시오. 이에 대한 구현 클래스를 만든 다음 인스턴스화하고 반환할 수 있습니다. 일반적으로 이것이 실제 응용 프로그램에 적용되는 방식입니다.

Spring Security 예제 WebSecurityConfigurer 구현

WebSecurityConfigurer 인터페이스를 구현하거나 기본 구현 클래스 WebSecurityConfigurerAdapter를 확장하고 메서드를 재정의할 수 있습니다. SecurityConfig.java

package com.journaldev.webapp.spring.security;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import com.journaldev.webapp.spring.dao.AppUserDetailsServiceDAO;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	@Override
	public void configure(AuthenticationManagerBuilder auth)
			throws Exception {

		// in-memory authentication
		// auth.inMemoryAuthentication().withUser("pankaj").password("pankaj123").roles("USER");

		// using custom UserDetailsService DAO
		// auth.userDetailsService(new AppUserDetailsServiceDAO());

		// using JDBC
		Context ctx = new InitialContext();
		DataSource ds = (DataSource) ctx
				.lookup("java:/comp/env/jdbc/MyLocalDB");

		final String findUserQuery = "select username,password,enabled "
				+ "from Employees " + "where username = ?";
		final String findRoles = "select username,role " + "from Roles "
				+ "where username = ?";
		
		auth.jdbcAuthentication().dataSource(ds)
				.usersByUsernameQuery(findUserQuery)
				.authoritiesByUsernameQuery(findRoles);
	}
	
	@Override
    public void configure(WebSecurity web) throws Exception {
        web
            .ignoring()
                // Spring Security should completely ignore URLs ending with .html
                .antMatchers("/*.html");
    }

}

configure(WebSecurity web) 메서드를 재정의하여 모든 HTML 파일을 무시한다는 점에 유의하십시오. 이 코드는 JDBC 인증을 플러그인하는 방법을 보여줍니다. DataSource를 제공하여 구성해야 합니다. 사용자 지정 테이블을 사용하고 있으므로 사용자 세부 정보와 역할을 가져오기 위해 선택 쿼리도 제공해야 합니다. 메모리 내 및 DAO 기반 인증 구성은 쉽고 위의 코드에 주석이 있습니다. 주석을 제거하여 사용할 수 있으며 한 번에 하나의 구성만 있는지 확인하십시오. @Configuration@EnableWebSecurity 주석이 필요하므로 스프링 프레임워크는 이 클래스가 스프링 보안 구성에 사용된다는 것을 알 수 있습니다. Spring Security Configuration은 Builder Pattern을 사용하고 있으며 인증 방법을 기반으로 일부 방법은 나중에 사용할 수 없습니다. 예를 들어 auth.userDetailsService()UserDetailsService의 인스턴스를 반환하고 그 뒤에 DataSource를 설정할 수 없는 것과 같은 다른 옵션을 가질 수 없습니다.

Spring Security Web과 Servlet API 통합

마지막 부분은 Spring Security 구성 클래스를 Servlet API에 통합하는 것입니다. 이는 AbstractSecurityWebApplicationInitializer 클래스를 확장하고 슈퍼 클래스 생성자에서 Security 구성 클래스를 전달하여 쉽게 수행할 수 있습니다. SecurityWebApplicationInitializer.java

package com.journaldev.webapp.spring.security;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class SecurityWebApplicationInitializer extends
		AbstractSecurityWebApplicationInitializer {

	public SecurityWebApplicationInitializer() {
        super(SecurityConfig.class);
    }
}

컨텍스트가 시작되면 ServletContext를 사용하여 Servlet Filter를 추가합니다. 이것은 Servlet-3 불만 서블릿 컨테이너에서만 작동합니다. 따라서 Apache Tomcat을 사용하는 경우 버전이 7.0 이상인지 확인하십시오. 프로젝트가 준비되었습니다. 원하는 서블릿 컨테이너에 배포하기만 하면 됩니다. 이 애플리케이션을 실행하기 위해 Apache Tomcat-7을 사용하고 있습니다. 아래 이미지는 다양한 시나리오에서의 응답을 보여줍니다.

보안 없이 HTML 페이지 액세스

잘못된 자격 증명으로 인한 인증 실패

Spring Security JDBC 인증을 사용하는 홈페이지

Spring Security UserDetailsService DAO 인증을 사용하는 홈 페이지

Spring Security 인메모리 인증을 사용하는 홈 페이지

로그아웃 페이지

Spring 서블릿 보안 프로젝트 다운로드