如何解决JAX-RS-EJB中的SecurityContext为空
我的后端JAX-RS应用程序中存在与我的EJB中的SecurityContext null有关的问题。
该应用程序涉及Users和Todo对象。一个用户可以有许多待办事项,这些待办事项基本上是表示便利贴的对象,描述了要做的事情。
我的项目中类的一般结构如下:
前端------> SecurityFilter->资源类(JAX-RS)->服务类(EJB)-> DB
对于此特定示例,这些类是:
前端------> SecurityFilter.java -> TodoRest.java(JAX-RS)-> TodoService.java(EJB)->数据库
将方法公开到前端的资源类称为“ TodoRest”,它具有一个名为getTodosByTodoUser的方法,该方法返回用户的所有待办事项。
由于此方法使用@Authz进行注释(报告如下:它是将方法链接到我的SecurityFilter的注释类),因此当我们调用它时,将触发SecurityFilter类。
这是 SecurityFilter 类:
package academy.learnprogramming.config;
import academy.learnprogramming.services.SecurityUtil;
import academy.learnprogramming.utils.Constants;
import io.jsonwebtoken.Jwts;
import org.apache.log4j.Logger;
import javax.annotation.Priority;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.Priorities;
import java.io.IOException;
import java.security.Key;
import java.security.Principal;
@Provider
@Authz
@Priority(Priorities.AUTHENTICATION)
public class SecurityFilter implements ContainerRequestFilter {
@Inject
private Logger LOG;
@Inject
SecurityUtil securityUtil;
@Inject
private ApplicationState applicationState;
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
String authHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
if(authHeader == null || authHeader.isEmpty() || !authHeader.startsWith(Constants.AUTH_TOKEN_BEARER_PREFIX)) {
LOG.error("Wrong or no authorization header found: " + authHeader);
//TODO - Beautify this exception
throw new NotAuthorizedException(Response
.status(Response.Status.UNAUTHORIZED)
.entity("No authorization header provided")
.build());
}
String token = authHeader.substring(Constants.AUTH_TOKEN_BEARER_PREFIX.length()).trim();
String email = applicationState.getEmail();
try {
//TODO - Parse the token
LOG.info("applicationState.getEmail(): " + email);
Key key = securityUtil.generateKey(applicationState.getEmail());
//If it doesn't throw exception,then the token is valid
Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody()
.getSubject()
.equals(applicationState.getEmail());
LOG.info("originalSecurityContext.getUserPrincipal before the token setting: " + requestContext.getSecurityContext().getUserPrincipal());
SecurityContext originalSecurityContext = requestContext.getSecurityContext();
requestContext.setSecurityContext(new SecurityContext() { // <---------- THIS IS THE CHANGE OF THE SECURITY CONTEXT
@Override
public Principal getUserPrincipal() {
return new Principal() {
@Override
public String getName() {
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody().getSubject();
}
};
}
@Override
public boolean isUserInRole(String role) {
return originalSecurityContext.isUserInRole(role);
}
@Override
public boolean isSecure() {
return originalSecurityContext.isSecure();
}
@Override
public String getAuthenticationScheme() {
return originalSecurityContext.getAuthenticationScheme();
}
});
LOG.info("securityContext.getUserPrincipal after the token setting: " + requestContext.getSecurityContext().getUserPrincipal().getName());
LOG.debug("User identified by mail " + applicationState.getEmail() + " - Token parsed successfully: " + token);
LOG.info("Token parsed successfully");
} catch (Exception e) {
LOG.error("Invalid token: " + token);
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
}
}
}
SecurityFilter类成功更改了请求的SecurityContext,将用户电子邮件作为主体插入。实际上,我在TodoRest类中注入了SecurityContext(带有@Context注释)(在下面进行报告),并且在执行过滤器之后,我在getTodosByTodoUser方法中记录了securityContext.getUserPrincipal(),该方法正确地返回了请求的用户电子邮件。
这是JAX-RS TodoRest 类:
package academy.learnprogramming.rest;
import academy.learnprogramming.config.Authz;
import academy.learnprogramming.dto.TodoDTO;
import academy.learnprogramming.entities.Todo;
import academy.learnprogramming.entities.TodoUser;
import academy.learnprogramming.services.TodoService;
import academy.learnprogramming.services.TodoUserService;
import academy.learnprogramming.utils.Constants;
import org.apache.log4j.Logger;
import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
@Path("todo")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class TodoRest {
@Inject
private Logger LOG;
@Inject
TodoService todoService;
@Inject
TodoUserService todoUserService;
@Context
SecurityContext securityContext;
@Authz
@Path("new")
@POST
public Response createTodo(Todo todo) {
LOG.info("rest service - createTodo");
todoService.createTodo(todo);
return Response.ok(todo).build();
}
@Authz
@Path("todosByTodoUser")
@GET
public List<Todo> getTodosByTodoUser(TodoUser todoUser) {
LOG.info("securityContext: " + securityContext.getUserPrincipal().getName()); // <---------- HERE I LOG THE SECURITY CONTEXT,WHICH IS CORRECTLY CHANGED BY SECURITY FILTER CLASS
return todoService.getAllTodos();
}
}
现在在getTodosByTodoUser中,我将调用TodoService EJB,并在其中注入了SecurityContext。可悲的是,在EJB内部,SecurityContext是nullB,我无法检索到getUserPrincipal()。
这是EJB TodoService 类:
package academy.learnprogramming.services;
import academy.learnprogramming.entities.Todo;
import academy.learnprogramming.entities.TodoUser;
import org.apache.log4j.Logger;
import org.dozer.DozerBeanMapper;
import javax.annotation.PostConstruct;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.SecurityContext;
import java.util.List;
@Stateless
public class TodoService {
@Inject
private Logger LOG;
@Inject
private EntityManager entityManager;
@Inject
private TodoUserService todoUserService;
@Inject
private DozerBeanMapper dozenBeanMapper;
@Context
SecurityContext securityContext;
private String todoUserEmail;
@PostConstruct
private void init() {
//TODO - Get user mail
todoUserEmail = "";
}
public Todo createTodo(Todo todo) {
//Persist into db
TodoUser todoUser = todoUserService.findUserByEmail(todo.getTodoOwner().getEmail());
if(todoUser != null) {
todo.setTodoOwner(todoUser);
todoUser.getTodos().add(todo);
entityManager.persist(todo);
}
return todo;
}
public List<Todo> getAllTodos() {
String securityContextEmail = securityContext.getUserPrincipal().getName(); // <---------- NULL,everything explodes
LOG.info("securityContextEmail:" + securityContextEmail);
List<Todo> todos = entityManager.createNamedQuery(Todo.TODO_FIND_ALL_TODOS_BY_OWNER,Todo.class)
.setParameter("email",securityContext.getUserPrincipal().getName())
.getResultList();
return todos;
}
public List<Todo> getAllTodosByTask(String taskText) {
return entityManager.createNamedQuery(Todo.TODO_FIND_TODO_BY_TASK,Todo.class)
.setParameter("task","%" + taskText + "%")
.setParameter("email","alessandro@provamail.com")
.getResultList();
}
}
为完整起见,我添加了 @Authz 注释类,该注释类将注释@Authz链接到SecurityFilter类:
package academy.learnprogramming.config;
import javax.ws.rs.NameBinding;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface Authz {
}
我在线检查了,发现:
- 有人在方法内部将securityContext从REST类传递到EJB服务类,但这非常丑陋,因为我们必须为每个方法传递securityContext;
- 有人使用@EJBContext,但是我尝试使用它而没有成功。
有人可以帮助我了解我在做什么错吗? 我特别想了解:
- 如何将安全上下文从Jax-RS类传播到ejb bean?
- 是否按照我的预期自动管理了安全信息?或..
- 是否需要改进或添加其他jboss-?. xml配置文件?或..
- 我是否需要在调用的Jax-RS Bean中进行某些更改,以便将安全信息传播到被调用的bean?或..
- 我是否假设有不正确的地方?
非常感谢您的宝贵时间。
编辑:我添加了此链接,他们说他们有解决办法,但引用的页面不再存在。 JAX-RS + EJB,SecurityContext inside ejb is null,on WildFly 10.1。 我尝试按照解决方案的建议进行操作,但是没有用。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。