Spring REST 自定义令牌认证示例

Spring REST 自定义令牌认证示例

原文: 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
}

下载源码

学习愉快!