原文: https://howtodoinjava.com/spring-restful/custom-token-auth-example/
通过使用 Spring REST 和 Spring Security 5 创建的方法,学习将 基于自定义令牌的身份验证 添加到 REST API。 将通过。 所有其他请求将返回HTTP 403
响应。
1. Spring Security 依赖
包括以下依赖项以使用 Spring Security 类和接口。
pom.xml
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
2. 扩展AbstractPreAuthenticatedProcessingFilter
创建一个类并扩展AbstractPreAuthenticatedProcessingFilter
。 它是用于处理过滤器的基类,这些过滤器处理预认证的认证请求,其中假定主体已经由外部系统认证。
默认情况下,当身份验证尝试失败时,过滤器链将继续进行,以允许其他身份验证机制处理请求。 如果发现令牌无效,它将有助于将请求传递给其他安全过滤器(例如,表单登录名)。
getPreAuthenticatedPrincipal()
方法有助于从当前请求中读取auth
标头值。
PreAuthTokenHeaderFilter.java
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.web.authentication
.preauth.AbstractPreAuthenticatedProcessingFilter;
public class PreAuthTokenHeaderFilter
extends AbstractPreAuthenticatedProcessingFilter {
private String authHeaderName;
public PreAuthTokenHeaderFilter(String authHeaderName) {
this.authHeaderName = authHeaderName;
}
@Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
return request.getHeader(authHeaderName);
}
@Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return "N/A";
}
}
这是可选方法。 应用程序可能会决定立即简单地返回认证失败错误。
3. 配置AuthenticationManager
并添加到HttpSecurity
我们需要设置身份验证管理器,它将处理身份验证过程并决定如何处理成功和失败方案。
添加身份验证管理器后,我们可以将PreAuthTokenHeaderFilter
添加到HttpSecurity
。
如果出现任何身份验证错误,则默认情况下将处理该错误ExceptionTranslationFilter
,该错误会在 Spring 转发到默认身份验证错误页面。 如果要以不同方式显示认证错误响应,则需要创建自定义ExceptionTranslationFilter
类。
AuthTokenSecurityConfig.java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
@Configuration
@EnableWebSecurity
@PropertySource("classpath:application.properties")
@Order(1)
public class AuthTokenSecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${howtodoinjava.http.auth.tokenName}")
private String authHeaderName;
//TODO: retrieve this token value from data source
@Value("${howtodoinjava.http.auth.tokenValue}")
private String authHeaderValue;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception
{
PreAuthTokenHeaderFilter filter = new PreAuthTokenHeaderFilter(authHeaderName);
filter.setAuthenticationManager(new AuthenticationManager()
{
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException
{
String principal = (String) authentication.getPrincipal();
if (!authHeaderValue.equals(principal))
{
throw new BadCredentialsException("The API key was not found "
+ "or not the expected value.");
}
authentication.setAuthenticated(true);
return authentication;
}
});
httpSecurity.
antMatcher("/api/**")
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(filter)
.addFilterBefore(new ExceptionTranslationFilter(
new Http403ForbiddenEntryPoint()),
filter.getClass()
)
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
4. 注册安全过滤器
传统上,spring security 在DelegatingFilterProxy
基于 XML 的配置中以web.xml
文件为起点。
web.xml
<!-- Spring Security -->
<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>
在 Java 配置中,我们可以通过删除类AbstractSecurityWebApplicationInitializer
来实现相同的效果。
SpringSecurityInitializer.java
import org.springframework.security.web.context
.AbstractSecurityWebApplicationInitializer;
public class SpringSecurityInitializer
extends AbstractSecurityWebApplicationInitializer {
//no code needed
}
4. Spring REST 自定义令牌认证演示
4.1. 标头中没有身份验证令牌
API 请求
HTTP GET http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/
API 响应
HTTP Status - 403 – Forbidden
Type Status - Report
Message Access - Denied
Description - The server understood the request but refuses to authorize it.
4.2. 标头中的身份验证令牌不正确
API 请求
HTTP GET http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/
AUTH_API_KEY: xyz123
API 响应
HTTP Status - 403 – Forbidden
Type Status - Report
Message Access - Denied
Description - The server understood the request but refuses to authorize it.
4.2. 标头中的身份验证令牌有效
API 请求
HTTP GET http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/
AUTH_API_KEY: abcd123456
API 响应
HTTP Status - 200 OK
{
//response body
}
学习愉快!