Spring Security提供了在web应用程序中执行身份验证和授权的方法。我们可以在任何基于servlet的web应用程序中使用spring安全性。
目录
Spring安全
- 久经考验的技术,使用它比改头换面更好。安全性是我们需要格外小心的地方,否则我们的应用程序将容易受到攻击者的攻击。
- 防止一些常见的攻击,如CSRF、会话固定攻击。
- 易于集成到任何web应用程序中。我们不需要修改web应用程序配置,spring会自动为web应用程序注入安全过滤器。
- 通过内存、DAO、JDBC、LDAP等多种方式提供对身份验证的支持。
- 提供忽略特定URL模式的选项,有利于提供静态HTML、图像文件。
- 支持组和角色。
Spring Security示例
我们将创建一个web应用程序并将其与Spring Security集成。
使用&8220;创建web应用程序;动态Web项目Eclipse中的选项,这样我们的框架web应用程序就准备好了。确保将其转换为maven项目,因为我们正在使用maven进行构建和部署。如果您不熟悉这些步骤,请参考Java Web应用程序教程.
一旦我们将有我们的应用程序安全,最终的项目结构将如下图所示。
我们将研究三种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
("pankaj", "pankaj123", 1);
INSERT INTO `Roles` (`username`, `role`)
VALUES
("pankaj", "Admin"),
("pankaj", "CEO");
commit;
我们还需要将JDBC数据源配置为servlet容器中的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框架相关的依赖项。
- Springjdbc:这是通过JDBC身份验证方法用于JDBC操作的。它需要将数据源设置为JNDI。有关它的完整用法示例,请参阅Spring数据源JNDI示例
- spring security标签库:Spring安全标记库,我用它在JSP页面中显示用户角色。大多数时候,你不需要它。
- Spring安全配置:用于配置认证提供者,是否使用JDBC、DAO、LDAP等。
- Spring安全网:此组件将Spring安全性集成到Servlet API。我们需要它将我们的安全配置插入到web应用程序中。
还请注意,我们将使用ServletAPI 3.0功能通过编程方式添加侦听器和过滤器,这就是为什么依赖项中的ServletAPI版本应该是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>
我已经包括index.jsp
as welcome-file
in the application deployment descriptor.
Spring Security处理CSRF攻击,所以当我们提交注销表单时,我们将CSRF令牌发送回服务器以删除它。Spring Security组件设置的CSRF对象是_csrf公司我们使用它的属性名和令牌值在注销请求中传递。
现在让我们看看Spring安全配置。
Spring安全示例userdetailsservicedao实现
由于我们还将使用基于DAO的身份验证,因此我们需要实现UserDetailsService
interface and provide the implementation for loadUserByUsername()
method.
理想情况下,我们应该使用一些资源来验证用户,但是为了简单起见,我只是在做基本的验证。
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
and returning it. You can create an implementation class for it and then instantiate and return it. Usually that is the way to go in actual applications.
Spring安全示例WebSecurityConfigurer实现
我们可以实施WebSecurityConfigurer
interface or we can extend the base implementation class WebSecurityConfigurerAdapter
and override the methods.
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)
method.
代码显示了如何插入JDBC身份验证。我们需要通过提供数据源来配置它。由于我们使用的是自定义表,因此我们还需要提供select查询来获取用户详细信息及其角色。
在内存中配置和基于DAO的身份验证很容易,上面的代码中对它们进行了注解。您可以取消注解以使用它们,请确保一次只能有一个配置。
@Configuration
and @EnableWebSecurity
annotations are required, so that spring framework know that this class will be used for spring security configuration.
Spring Security配置正在使用生成器模式基于authenticate方法,有些方法以后将不可用。例如,auth.userDetailsService()
returns the instance of UserDetailsService
and then we can’t have any other options, such as we can’t set DataSource after it.
将Spring Securityweb与Servlet API集成
最后一部分是将Spring安全配置类集成到Servlet API中。这可以很容易地通过扩展来实现AbstractSecurityWebApplicationInitializer
class and passing the Security configuration class in the super class constructor.
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来添加