编辑:这是一个史诗般的手掌情况。SessionScoped 的导入错误。昨晚检查它时太累了,我确定我正在使用企业会话范围导入,而我仍在使用面部会话范围导入。我把它留下来帮助像我这样的笨蛋。:)
这是该项目的早期阶段。在使用托管 bean 实现这一点之后,我将托管 bean 更改为 CDI bean,因为这似乎是关于最佳处理方式的最新共识,但这破坏了以前的工作代码。我终其一生都无法弄清楚为什么。帮助和建议表示赞赏。
快乐的道路(摘要......代码摘录下面的详细信息)
如果用户未登录,则显示登录或注册链接。
如果用户登录显示用户首选项或注销链接。
现在 CDI 的糟糕路径(我不怪 CDI)
如果用户未登录,则显示登录或注册链接。
如果用户登录仍然看到登录或注册链接。(糟糕,糟糕的应用程序)
涉及的对象是
- 一个 facelet 菜单面板(带有一个 primefaces 登录对话框......我认为这与它没有任何关系,但为了完整性而包括在内)如果登录或未登录,则带有渲染属性,
- 会话范围的用户 bean,
- 一个请求范围的身份验证 bean,用于登录和注销用户。
下面列出了使用的对象。实现为 CDI bean。
小面
<h:panelGroup id="loginPanel" rendered="#{!user.loggedIn}">
Show login buttons and stuff
</h:panelGroup>
<h:panelGroup id="logoutPanel" rendered="#{user.loggedIn}">
Show logout buttons and stuff
</h:panelGroup
身份验证 bean
@Named(value = "webAuthenticationBean") //formerly managedbean
@RequestScoped
public class WebAuthenticationBean implements Serializable {
@Inject private UserBean user; //formerly a managed property which worked
...
request.login(uername, password);
user.setuserdata(username); // sessionscoped user state here used to check login state among other things later.
...
return(true) // they are now logged in
用户 bean
@Named(value = "user") //formerly managedbean
@SessionScoped
public class UserBean implements Serializable {
@EJB
private UserService userService; //stateless session bean
private userInfo = new UserInfo(); // keeps user state and can be used as a DTO/VO
@PostConstruct
public void init() {
//sets default state to "guest user". This is NOT a logged in state
}
public void setuserdata(String username){
userInfo = userService.getUserInfo(username);
// method called from WebAuthenticationBean
// sets the user state to a non-guest user (they're logged in).
// I can see in debug mode that this is being called and retrieving
// the user data from the database and setting "userInfo"
}
public void isLoggedIn() throws InvalidUserException{
// checks state to see if they are logged in, basically a bit more than are they still a guest or not
returns (true) if logged in
returns (false) if not logged in
// this worked with managed beans
}
...
所以这是我在调试模式下观看时的实际用例:
Happy Path(更改为 CDI bean 之前)
1) 用户导航到欢迎页面
2) 查询用户 bean 以查看他们是否已登录(facelet 中的 user.loggedIn)。
3) userbean 检查登录状态。如果他们仍然是访客,则未登录。
4) 他们被标识为访客,因此 isLoggedIn() 返回 false。
5) 显示登录按钮。
6) 用户请求登录
7) 认证bean 开始登录过程:request.login 返回成功
8) authenticationbean 设置用户数据:user.setuserdata(username) 返回成功。
9)身份验证bean loginMethod返回(他们在服务器上记录了userprincipal)
此处的备用(蹩脚)路径分支(快乐路径继续)
10) 菜单重新检查登录状态 (user.loggedIn)
11) userbean 检查适当的状态并确定它们是有效的非访客用户
12) userbean 返回 (true) 他们已登录
13) 菜单显示注销按钮
糟糕的路径(我将这些更改为 CDI bean 后会发生什么)
10) 菜单重新检查登录状态 (user.loggedIn)
11) userbean 检查适当的状态并看到他们是访客 //更新的用户状态似乎在此会话中从该用户中消失了。
12) userbean 返回 (false) 他们没有登录 // 但他们是
13) 菜单显示登录按钮 // 他们无论如何都无法登录,因为服务器已经看到他们登录,在这个会话中 (ServletException: Attempt to re -在用户身份已经存在时登录)。
为什么使用 managedbeans 可以看到 userbean 在会话范围内维护其数据,但使用 cdi bean 却不能?我难住了。如果必须,我会切换回托管 bean,这不是一个大问题,但我想找出我搞砸了。
我在 UserBean 的 init 方法中添加了一些调试代码,看起来好像系统将 SessionScoped UserBean 视为 RequestScoped。那就是它在每次调用时都进行初始化。
@PostConstruct
public void init() {
if (userInfo == null) {
userInfo = new UserInfoDTO();
userInfo.setUserName("Guest");
List<String> guestGroup = Arrays.asList(CoreUserGroupType.GUEST.toString());
userInfo.setUserGroups(guestGroup);
System.out.println("UserBean.init INSIDE Init If Statement");
}
System.out.println("UserBean.init OUTSIDE Init If Statement");
}
如果它真的像 SessionScoped 一样,那么 userInfo 对象就不会每次都为空,并且不会每次都执行“if”语句。但它会在每次调用 UserBean 时执行。所以这是问题的症结所在。事实上,如果它的行为就像在会话范围内,它不会在每次调用时都命中 init 方法,因为它仍然会被初始化。
我没有正确创建会话范围的 bean 吗?看起来是这样,但我不知道如何。如前所述,此代码在定义为 managedbean 时运行良好。