# Spring REST 自定义令牌认证示例

> 原文： [https://howtodoinjava.com/spring-restful/custom-token-auth-example/](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`

```java
<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`](https://github.com/spring-projects/spring-security/blob/master/web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java)。 它是用于处理过滤器的基类，这些过滤器处理**预认证的**认证请求，其中假定主体已经由外部系统认证。

默认情况下，当身份验证尝试失败时，过滤器链将继续进行，以允许其他身份验证机制处理请求。 如果发现令牌无效，它将有助于将请求传递给其他安全过滤器（例如，表单登录名）。

`getPreAuthenticatedPrincipal()`方法有助于从当前请求中读取`auth`标头值。

`PreAuthTokenHeaderFilter.java`

```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`](https://docs.spring.io/spring-security/site/docs/4.2.11.RELEASE/apidocs/org/springframework/security/web/access/ExceptionTranslationFilter.html)，该错误会在 Spring 转发到默认身份验证错误页面。 如果要以不同方式显示认证错误响应，则需要创建自定义`ExceptionTranslationFilter`类。

`AuthTokenSecurityConfig.java`

```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`

```java
<!-- 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`

```java
import org.springframework.security.web.context
			.AbstractSecurityWebApplicationInitializer;
public class SpringSecurityInitializer 
			extends AbstractSecurityWebApplicationInitializer {
    //no code needed
}

```

## 4\. Spring REST 自定义令牌认证演示

#### 4.1. 标头中没有身份验证令牌

`API 请求`

```java
HTTP GET http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/

```

`API 响应`

```java
HTTP Status - 403 – Forbidden
Type Status - Report
Message Access - Denied
Description - The server understood the request but refuses to authorize it.

```

#### 4.2. 标头中的身份验证令牌不正确

`API 请求`

```java
HTTP GET http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/

AUTH_API_KEY: xyz123

```

`API 响应`

```java
HTTP Status - 403 – Forbidden
Type Status - Report
Message Access - Denied
Description - The server understood the request but refuses to authorize it.

```

#### 4.2. 标头中的身份验证令牌有效

`API 请求`

```java
HTTP GET http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/

AUTH_API_KEY: abcd123456

```

`API 响应`

```java
HTTP Status - 200 OK

{
	//response body
}

```

[下载源码](https://howtodoinjava.com/wp-content/downloads/SpringRestExample.zip)

学习愉快！