永远不会调用 JAX-RS Provider 的过滤方法
Filter method of JAX-RS Provider is never called
我尝试遵循 this 教程并实现了示例中给出的 Provider class。
@Provider
@Secured
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
private static final String REALM = "example";
private static final String AUTHENTICATION_SCHEME = "Bearer";
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// Get the Authorization header from the request
String authorizationHeader =
requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
// Validate the Authorization header
if (!isTokenBasedAuthentication(authorizationHeader)) {
abortWithUnauthorized(requestContext);
return;
}
// Extract the token from the Authorization header
String token = authorizationHeader
.substring(AUTHENTICATION_SCHEME.length()).trim();
try {
// Validate the token
validateToken(token);
} catch (Exception e) {
abortWithUnauthorized(requestContext);
}
}
private boolean isTokenBasedAuthentication(String authorizationHeader) {
// Check if the Authorization header is valid
// It must not be null and must be prefixed with "Bearer" plus a whitespace
// The authentication scheme comparison must be case-insensitive
return authorizationHeader != null && authorizationHeader.toLowerCase()
.startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " ");
}
private void abortWithUnauthorized(ContainerRequestContext requestContext) {
// Abort the filter chain with a 401 status code response
// The WWW-Authenticate header is sent along with the response
requestContext.abortWith(
Response.status(Response.Status.UNAUTHORIZED)
.header(HttpHeaders.WWW_AUTHENTICATE,
AUTHENTICATION_SCHEME + " realm=\"" + REALM + "\"")
.build());
}
private void validateToken(String token) throws Exception {
// Check if the token was issued by the server and if it's not expired
// Throw an Exception if the token is invalid
//TODO validate against database
if(!token.equals("exampleString")) {
throw new AuthenticationException("Invalid token.");
}
}
}
这个Provider class也被服务器识别,可以在日志中看到:
Dez 25, 2017 6:47:09 PM com.sun.jersey.api.core.ScanningResourceConfig logClasses
INFORMATION: Root resource classes found:
class com.jwt.service.AuthenticationEndpoint
class com.jwt.service.HelloWorldService
Dez 25, 2017 6:47:09 PM com.sun.jersey.api.core.ScanningResourceConfig logClasses
INFORMATION: Provider classes found:
class com.jwt.service.AuthenticationFilter
我为注释创建了一个接口:
@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Secured {
}
并且还注释了我的 REST 端点。
@GET
@Path("/secured")
@Secured
public Response getUserSecured() {
return Response.status(200).entity("Secured getUser is called").build();
}
即使我的 Provider class 被识别了,它在调用注释方法时仍然没有做任何事情。即使没有发送任何身份验证,也不会输出未经授权的状态代码,而是返回“Secured getUser is called”。
有人知道为什么我的 Provider class 的过滤器方法从未被调用过吗?
com.sun.jersey.api
包(在提供的日志中可见)是 Jersey 1,而您正在学习的教程涉及 jersey 2。
升级到 Jersey 2 即可。
我尝试遵循 this 教程并实现了示例中给出的 Provider class。
@Provider
@Secured
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
private static final String REALM = "example";
private static final String AUTHENTICATION_SCHEME = "Bearer";
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// Get the Authorization header from the request
String authorizationHeader =
requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
// Validate the Authorization header
if (!isTokenBasedAuthentication(authorizationHeader)) {
abortWithUnauthorized(requestContext);
return;
}
// Extract the token from the Authorization header
String token = authorizationHeader
.substring(AUTHENTICATION_SCHEME.length()).trim();
try {
// Validate the token
validateToken(token);
} catch (Exception e) {
abortWithUnauthorized(requestContext);
}
}
private boolean isTokenBasedAuthentication(String authorizationHeader) {
// Check if the Authorization header is valid
// It must not be null and must be prefixed with "Bearer" plus a whitespace
// The authentication scheme comparison must be case-insensitive
return authorizationHeader != null && authorizationHeader.toLowerCase()
.startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " ");
}
private void abortWithUnauthorized(ContainerRequestContext requestContext) {
// Abort the filter chain with a 401 status code response
// The WWW-Authenticate header is sent along with the response
requestContext.abortWith(
Response.status(Response.Status.UNAUTHORIZED)
.header(HttpHeaders.WWW_AUTHENTICATE,
AUTHENTICATION_SCHEME + " realm=\"" + REALM + "\"")
.build());
}
private void validateToken(String token) throws Exception {
// Check if the token was issued by the server and if it's not expired
// Throw an Exception if the token is invalid
//TODO validate against database
if(!token.equals("exampleString")) {
throw new AuthenticationException("Invalid token.");
}
}
}
这个Provider class也被服务器识别,可以在日志中看到:
Dez 25, 2017 6:47:09 PM com.sun.jersey.api.core.ScanningResourceConfig logClasses
INFORMATION: Root resource classes found:
class com.jwt.service.AuthenticationEndpoint
class com.jwt.service.HelloWorldService
Dez 25, 2017 6:47:09 PM com.sun.jersey.api.core.ScanningResourceConfig logClasses
INFORMATION: Provider classes found:
class com.jwt.service.AuthenticationFilter
我为注释创建了一个接口:
@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Secured {
}
并且还注释了我的 REST 端点。
@GET
@Path("/secured")
@Secured
public Response getUserSecured() {
return Response.status(200).entity("Secured getUser is called").build();
}
即使我的 Provider class 被识别了,它在调用注释方法时仍然没有做任何事情。即使没有发送任何身份验证,也不会输出未经授权的状态代码,而是返回“Secured getUser is called”。
有人知道为什么我的 Provider class 的过滤器方法从未被调用过吗?
com.sun.jersey.api
包(在提供的日志中可见)是 Jersey 1,而您正在学习的教程涉及 jersey 2。
升级到 Jersey 2 即可。