原文: 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 PreAuthTokenHeaderFilterextends AbstractPreAuthenticatedProcessingFilter {private String authHeaderName;public PreAuthTokenHeaderFilter(String authHeaderName) {this.authHeaderName = authHeaderName;}@Overrideprotected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {return request.getHeader(authHeaderName);}@Overrideprotected 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;@Overrideprotected void configure(HttpSecurity httpSecurity) throws Exception{PreAuthTokenHeaderFilter filter = new PreAuthTokenHeaderFilter(authHeaderName);filter.setAuthenticationManager(new AuthenticationManager(){@Overridepublic 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 SpringSecurityInitializerextends 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 – ForbiddenType Status - ReportMessage Access - DeniedDescription - 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 – ForbiddenType Status - ReportMessage Access - DeniedDescription - 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}
学习愉快!
