Spring安全示例教程

时间:2020-02-23 14:36:01  来源:igfitidea点击:

Spring Security提供了在Web应用程序中执行身份验证和授权的方法。
我们可以在任何基于servlet的Web应用程序中使用spring security。

Spring安全

使用Spring Security的一些好处是:

  • 经过验证的技术,最好使用此技术,而不是重新发明轮子。
    安全是我们需要格外注意的事情,否则我们的应用程序将容易受到攻击者的攻击。

  • 防止某些常见的攻击,例如CSRF,会话固定攻击。

  • 易于集成到任何Web应用程序中。
    我们不需要修改Web应用程序配置,Spring会自动将安全过滤器注入Web应用程序。

  • 通过不同的方式提供对身份验证的支持-内存,DAO,JDBC,LDAP等。

  • 提供忽略特定URL模式的选项,非常适合提供静态HTML,图像文件。

  • 支持组和角色。

Spring安全示例

我们将创建一个Web应用程序并将其与Spring Security集成。

使用Eclipse中的"动态Web项目"选项创建一个Web应用程序,以便我们的框架Web应用程序已准备就绪。
确保将其转换为Maven项目,因为我们使用Maven进行构建和部署。
如果您不熟悉这些步骤,请参考Java Web Application Tutorial。

一旦我们确保了应用程序的安全,最终的项目结构将如下图所示。

我们将研究三种Spring安全认证方法。

  • 在记忆中
  • 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
	('hyman', 'hyman123', 1);

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

commit;

我们还需要在servlet容器中将JDBC数据源配置为JNDI,要了解这一点,请阅读Tomcat JNDI数据源示例。

Spring Security Maven依赖关系

这是我们最终的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。
    有关其用法的完整示例,请参阅Spring DataSource JNDI示例

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

我已经将index.jsp作为welcome-file包含在应用程序部署描述符中。

Spring Security负责CSRF攻击,因此,当我们提交注销表单时,我们会将CSRF令牌发送回服务器以将其删除。
Spring Security组件设置的CSRF对象是_csrf,我们正在使用它的属性名称和令牌值在注销请求中传递。

现在让我们看一下Spring Security的配置。

Spring Security示例UserDetailsService DAO实现

由于我们还将使用基于DAO的身份验证,因此我们需要实现" UserDetailsService"接口,并提供" loadUserByUsername()"方法的实现。

理想情况下,我们应该使用一些资源来验证用户,但是为了简单起见,我只是在进行基本验证。

AppUserDetailsServiceDAO.java

package com.theitroad.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("hyman")){
			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 "hyman123";
			}
			
			@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.theitroad.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.theitroad.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("hyman").password("hyman123").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框架知道此类将用于Spring安全配置。

Spring Security Configuration使用的是Builder模式,并且基于authenticate方法,某些方法稍后将不可用。
例如," auth.userDetailsService()"返回" UserDetailsService"的实例,那么我们就没有其他选择,例如,我们不能在其后设置DataSource。

将Spring Security Web与Servlet API集成

最后一部分是将我们的Spring Security配置类集成到Servlet API。
这可以通过扩展AbstractSecurityWebApplicationInitializer类并在超类构造函数中传递Security配置类来轻松完成。

SecurityWebApplicationInitializer.java

package com.theitroad.webapp.spring.security;

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

public class SecurityWebApplicationInitializer extends
		AbstractSecurityWebApplicationInitializer {

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

当我们的上下文启动时,它使用ServletContext添加ContextLoaderListener侦听器并将我们的配置类注册为Servlet过滤器。

注意,这仅在Servlet-3投诉Servlet容器中有效。
因此,如果您使用的是Apache Tomcat,请确保其版本为7.0或者更高。

我们的项目已经准备就绪,只需将其部署到您喜欢的servlet容器中即可。
我正在使用Apache Tomcat-7来运行此应用程序。