Spring Securityは、Webアプリケーションでの認証と認可を実行する方法を提供します。Spring Securityは、任意のサーブレットベースのWebアプリケーションで使用することができます。
Spring Security
Spring Securityを使用することのいくつかの利点は、次のとおりです:
- 証明された技術であり、車輪を再発明するよりも使用した方が良いです。セキュリティは、特別な注意を払わないと攻撃者に対して脆弱なアプリケーションになる可能性があるため、追加の注意が必要です。
- CSRFやセッション固定攻撃など、一部の一般的な攻撃を防ぎます。
- どのWebアプリケーションにも簡単に統合できます。Webアプリケーションの設定を変更する必要はありません。Springは自動的にセキュリティフィルタをWebアプリケーションに注入します。
- インメモリ、DAO、JDBC、LDAPなど、さまざまな方法での認証をサポートします。
- 特定のURLパターンを無視するオプションがあり、静的なHTMLや画像ファイルを提供するのに適しています。
- グループとロールのサポートがあります。
Spring Securityの例
私たちはウェブアプリケーションを作成し、それをSpring Securityと統合します。Eclipseの「Dynamic Web Project」オプションを使用してウェブアプリケーションを作成し、スケルトンのウェブアプリケーションを準備します。ビルドと展開にはMavenを使用しているため、Mavenプロジェクトに変換することを確認してください。これらの手順に慣れていない場合は、Java Web Application Tutorialを参照してください。アプリケーションをセキュリティで保護した後、最終的なプロジェクト構造は以下の画像のようになります。私たちは3つのSpring Security認証方法を見ていきます。
- インメモリ
- DAO
- 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;
また、ServletコンテナでJDBC DataSourceをJNDIとして構成する必要があります。これについては、Tomcat JNDI DataSource Exampleを読んでください。
Spring Security Maven Dependencies
ここに私たちの最終的な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に関連する依存関係を持っています。
- spring-jdbc:JDBC認証メソッドでのJDBC操作に使用されます。 JNDIとしてDataSourceの設定が必要です。使用例の詳細については、Spring DataSource JNDI Exampleを参照してください。
- spring-security-taglibs:Spring Securityタグライブラリです。JSPページでユーザーの役割を表示するために使用しました。ほとんどの場合、それは必要ありませんが、一部の場合には必要です。
- spring-security-config:認証プロバイダーの設定に使用され、JDBC、DAO、LDAPなどの使用を選択できます。
- spring-security-web:このコンポーネントはSpring SecurityをServlet APIに統合します。Webアプリケーションにセキュリティ設定をプラグインするために必要です。
また、プログラムでリスナーとフィルターを追加するためにServlet API 3.0の機能を使用する予定ですので、依存関係のservlet apiバージョンは3.0以上である必要があります。
Spring Securityの例のビューページ
アプリケーションにはJSPとHTMLページがあります。私たちはHTMLページ以外のすべてのページに認証を適用したいと考えています。health.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>
I have included index.jsp
as welcome-file
in the application deployment descriptor. Spring Security takes care of CSRF attack, so when we are submitting form for logout, we are sending the CSRF token back to server to delete it. The CSRF object set by Spring Security component is _csrf and we are using it’s property name and token value to pass along in the logout request. Let’s look at the Spring Security configurations now.
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");
}
//ダミーユーザーの詳細を作成し、JDBC操作を実行する必要があります。
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 auths = new java.util.ArrayList();
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 {
// インメモリ認証
// auth.inMemoryAuthentication().withUser("pankaj").password("pankaj123").roles("USER");
// カスタムのUserDetailsService DAOを使用
// auth.userDetailsService(new AppUserDetailsServiceDAO()); を使用
// 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は.htmlで終わるURLを完全に無視する必要があります
.antMatchers("/*.html");
}
}
注意:すべてのHTMLファイルをconfigure(WebSecurity web)
メソッドをオーバーライドして無視しています。このコードはJDBC認証をプラグインする方法を示しています。DataSourceを提供することでそれを設定する必要があります。カスタムテーブルを使用しているため、ユーザーの詳細と役割を取得するための選択クエリも提供する必要があります。インメモリとDAOベースの認証の設定は簡単ですが、上記のコードではコメントアウトされています。それらを使用するためにはコメントアウトを解除してください。ただし、一度に1つの設定のみを使用するようにしてください。@Configuration
アノテーションと@EnableWebSecurity
アノテーションが必要です。これにより、SpringフレームワークがこのクラスがSpring Securityの設定に使用されることを知ることができます。Spring Securityの設定はビルダーパターンを使用しており、authenticateメソッドに基づいて、一部のメソッドは後で使用できなくなります。たとえば、auth.userDetailsService()
はUserDetailsService
のインスタンスを返し、その後DataSourceを設定することはできません。
Spring Security WebとServlet APIの統合
最後の部分は、Spring Securityの設定クラスをServlet APIに統合することです。これは、AbstractSecurityWebApplicationInitializer
クラスを拡張し、スーパークラスのコンストラクターにセキュリティ設定クラスを渡すことで簡単に行うことができます。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を使用してContextLoaderListenerリスナーを追加し、設定クラスをServlet Filterとして登録します。ただし、これはServlet-3に対応したサーブレットコンテナでのみ動作します。したがって、Apache Tomcatを使用している場合は、バージョンが7.0以上であることを確認してください。プロジェクトは準備ができており、お気に入りのサーブレットコンテナにデプロイするだけです。このアプリケーションでは、Apache Tomcat-7を使用して実行しています。以下の画像は、さまざまなシナリオでの応答を示しています。
セキュリティなしでHTMLページにアクセスする
認証エラー(無効な資格情報)
Spring Security JDBC認証でのホームページへのアクセス
Spring Security UserDetailsService DAO認証を使用したホームページ
Spring Security In-Memory認証を使用したホームページ
ログアウトページ
もしServlet Specs 3をサポートしていないServletコンテナを使用する場合、デプロイメント記述子を介して
DispatcherServlet
を登録する必要があります。詳細については、WebApplicationInitializer
のJavaDocを参照してください。これで、Spring Securityの例のチュートリアルとServletベースのWebアプリケーションへの統合が完了しました。詳細を学ぶために、以下のリンクからサンプルプロジェクトをダウンロードして試してみてください。
Source:
https://www.digitalocean.com/community/tutorials/spring-security-example-tutorial