JSF permanent redirect using a bean on a preRenderView event - jsf

Is there a way to permanently redirect to a page on preRenderEvent view using faces-config navigation?
For example, after login it will redirect on a page and on that page I need to know the role of a user so that I can redirect him to the correct page.
I'm using this to redirect:
<f:metadata>
<f:event listener="#{loginRedirectBean.redirect}" type="preRenderView"></f:event>
</f:metadata>
public void redirect() throws IOException {
if (identity.isLoggedIn()) {
if(hasRole("admin")) {
ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
context.redirect("faces.admin");
}
}
}

You should so make use of "if else" statements based on the user role :
public void redirect(){
ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
if (context.isUserInRole("admin"){
context.redirect("adminpage");
}
else if (context.isUserInRole("user"){
context.redirect("userpage");
}
// other possible roles
else context.redirect("homepage");
}
Unrelated : In order to avoid firing double requests to the server whenever a user is logging in, I suggest to transfer that logic to be implemented within the method redirect(), to the method responsible of user logging with "if else" statements and with a String returning outcome (without the need to redirect manually as above) to specify the right page name to redirct to depending on the user role in "if" instruction.

Since faces-config navigation. I've just used a file given a role.
public void redirect() throws IOException {
String redirectUrl = "login.jsf";
if (identity.isLoggedIn()) {
try {
User user = ((PicketlinkUser) identity.getAccount()).getUser();
if (user.hasRole(Role.ROLE_ADMIN)) {
redirectUrl = "pages/secured/" + Role.ROLE_ADMIN + "/index.jsf";
} else if (user.hasRole(Role.ROLE_BRIDE)) {
redirectUrl = "pages/secured/" + Role.ROLE_BRIDE + "/index.jsf";
} else if (user.hasRole(Role.ROLE_VENDOR)) {
redirectUrl = "pages/secured/" + Role.ROLE_VENDOR + "/index.jsf";
} else if (user.hasRole(Role.ROLE_COLLABORATOR)) {
redirectUrl = "pages/secured/" + Role.ROLE_COLLABORATOR + "/index.jsf";
} else {
log.info("user={} has no valid role?", user);
redirectUrl = "index.jsf";
}
} catch (NullPointerException e) {
log.error("no role?={}", e.getMessage());
}
} else {
log.error("isNotLoggedIn, redirect to login.");
}
ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
context.redirect(redirectUrl);
}

Related

How to navigate to different pages on the basis of query params by jsf page initialization in the corresponding controller

#Factory("loginContext")
#Begin(join = true)
public LoginContext initLoginContext() {
if (loginContext == null) {
loginContext = new LoginContext();
}
loginContext.setLanguageCode(LocaleSelector.instance().getLanguage());
checkEnvironment();
Map<String,String> requestParams = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
String prodCode=requestParams.get("prodCode");
String token=requestParams.get("token");
//token validation
if (token.equals("admin")) {
System.out.println("admin page");
} else if (token.equals("user")) {
System.out.println("user page");
} else {
return loginContext;
}
}
here i will be getting user credentials in token(JWE string) and then some validation of on user is done(till here code is fine and i am clear how to do this). later on on the basis of user privilege, i will navigate directly to related page for user(instead of loading the login page related to controller)
I am stuck here, Pls suggest something
in the LoginController i added following code which worked for me.:
#Factory("loginContext")
#Begin(join = true)
public LoginContext initLoginContext() {
if (loginContext == null) {
loginContext = new LoginContext();
}
loginContext.setLanguageCode(LocaleSelector.instance().getLanguage());
Map<String,String> requestParams =
FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
String prodCode=requestParams.get("prodCode");
String token=requestParams.get("token");
//token validation
loginContext.setToken(token);
loginContext.setProductCode(prodCode);
return loginContext;
}
#Factory(value = "redirectWelcome", autoCreate = true, scope = ScopeType.EVENT)
public boolean isRedirectWelcome()
{
boolean welRet=false;
if(loginContext.isLoggedIn()==false)
{
String loginReturn=login();// login is customized method as per my requirement
if(loginReturn.equals("loggedIn"))
{
FacesContext.getCurrentInstance().getApplication().getNavigationHandler().handleNavigation(FacesContext.getCurrentInstance(),
null, "/myservice/auth/showWelcome/welcome.jspx?faces-redirect=true");
loginContext.setLoggedIn(true);
welRet=true;
}}
return welRet;
}
then In JSP page(login.jspx) i added following in the body.
<h:outputText value="Login bypass..." rendered="#{loginController.redirectWelcome}" />

Bean doesn't dispatch to another page

I've a problem with a bean that doesn't dispatch the response to another page.
This is the code:
#ManagedBean(name = "ssoServiceBean")
public class SSOServiceBean {
#ManagedProperty(value="#{param.samlRequest}")
private String samlRequest;
#ManagedProperty(value="#{param.relayState}")
private String relayState;
#PostConstruct
public void submit() {
System.out.println("1) PostConstruct method called");
//samlRequest = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("samlRequest");
//relayState = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("relayState");
processResponse();
}
//getters and setters omitted for succinctness
private void processResponse(){
System.out.println("2) Processing response");
String uri;
if(samlRequest != null && !samlRequest.equals("") && relayState != null && !relayState.equals("")) {
System.out.println("SAMLRequest: "+samlRequest);
System.out.println("RelayState: "+relayState);
uri = "challenge.xhtml";
System.out.println("3) Sending challenge...");
} else {
uri = "dashboard.xhtml";
System.out.println("3) Sending dashboard...");
}
try {
FacesContext.getCurrentInstance().getExternalContext().dispatch(uri);
System.out.println("4) Done.");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
The problem is that the dispatch() method doesn't work properly, and seems to be ignored.
Infact the system responses with an error of the related bean's page ssoservice.xhtml
I've used the Postconstruct annotation because with this bean I've to intercept POST parameters that come from a third-party page.
Once I've received the post parameters, I've to render the challenge.xhtml page, WITHOUT using a redirect directive.
Nextly, the user will submit challenge.xhtml to the related bean ChallengeBean.java .
So, what is the problem? Why dispatch doesn't work?

How to restrict access if user is not logged in

My project has a template main.xhtml and three views login.xhtml, dashboard.xhtml, new.xhtml. Once I login in login.xhtml, the LoginBean will validate and if successful, then it will take to dashboard.xhtml. If user need to create an new record he click the new button which takes to new.xhtml.
But the problem is, if dashboard.xhtml is requested directly from browser, then it is working without login. Do I need to check every view that the user is logged in? How can I achieve this?
It sounds like as if you're homegrowing authentication. In that case, you need to also homegrow access restriction. That is normally to be done using a servlet filter.
Assuming that you're logging in as follows in a #RequestScoped bean,
public String login() {
User user = userService.find(username, password);
FacesContext context = FacesContext.getCurrentInstance();
if (user != null) {
context.getExternalContext().getSessionMap().put("user", user);
return "dashboard.xhtml?faces-redirect=true";
} else {
context.addMessage(null, new FacesMessage("Unknown login, try again."));
return null;
}
}
Then you can check for the logged-in user in a #WebFilter("/*") filter as follows:
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
User user = (session != null) ? session.getAttribute("user") : null;
String loginURL = request.getContextPath() + "/login.xhtml";
boolean loginRequest = request.getRequestURI().startsWith(loginURL);
boolean resourceRequest = request.getRequestURI().startsWith(request.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER);
if (user != null || loginRequest || resourceRequest)) {
chain.doFilter(request, response);
} else {
response.sendRedirect(loginURL);
}
}
Note thus that this would continue the request when the user is logged in, or when the login page itself is requested directly, or when a JSF resource (CSS/JS/image) is been requested.
If you were using container managed authentication, then the filter would have been unnecessary. See also How to handle authentication/authorization with users in a database?

How to use HttpServletRequest#login() programmatic login with SHA-256 configured security realm

i have read there, i am using glassfish 3.1.1 security realm configured with sha-256 digest algorithm. is there any tutorial about this ? maybe i am blethering, i am trying to login with this code:
public void login() throws NoSuchAlgorithmException {
FacesContext context = FacesContext.getCurrentInstance();
HttpServletRequest request = (HttpServletRequest)context.getExternalContext().getRequest();
EntityManager em = emf.createEntityManager();
boolean committed = false;
try {
FacesMessage msg = null;
EntityTransaction entr = em.getTransaction();
entr.begin();
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(password.getBytes());
byte byteData[] = md.digest();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < byteData.length; i++) {
sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
}
password = sb.toString();
Query query = em.createQuery("SELECT COUNT(u) FROM EntityUser u WHERE u.userName = :userName AND u.password = :password")
.setParameter("userName", userName).setParameter("password", password);
long result = (long)query.getSingleResult();
if (result == 1) {
request.login(userName, password);
msg = new FacesMessage();
msg.setSeverity(FacesMessage.SEVERITY_INFO);
msg.setSummary("You are logged in");
}
entr.commit();
committed = true;
} catch (ServletException e) {
context.addMessage(null, new FacesMessage("wrong username or password"));
}
finally {
if (!committed) entr.rollback();
}
} finally {
em.close();
}
}
result variable returns 1, but request.login(userName, password); method in if condition always throws servletexception.
Can you post the exception stacktrace? That way it would be easier to understand the source of the exception. But judging from your currently supplied code, you should supply in
request.login(userName, password);
the password as the plain-text password and not the hashed password.
Interface HttpServletRequest
ServletException - if the configured login mechanism does not support username password
authentication, or if a non-null caller identity had already been established (prior to
the call to login), or if validation of the provided username and password fails.
There can be a lot of reasons that login fails. You've just checked if appropriate user and password are in table. Glassfish makes two queries - in authenticate process - to two tables. One to table specified as userTable, and second to groupTable which are determined in security realm definition. Check if web.xml and glassfish-web.xml are correct too.
the questioned problem is whole about method
request.login(userName, password);
Author made everything right, even his own authentication way of working with users database, but request.login needs for authentication realm be set up, to be used by this method. And you have your own, you dont need separate request.login authentication. For the case you need it - thats how you do it jdbc-realm-setup-with-glassfish-v3
So, after you get the result=1, you set up your context.getExternalContext().getSessionMap().put("user", u);
and send redirection context.getExternalContext().redirect(context.getExternalContext().getRequestContextPath() + "какой-то модуль.xhtml");
and use webfilter to block access to /Pages/*.xhtml without logging in.
#WebFilter("/Pages/*")
public class LoggingFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse res = (HttpServletResponse)response;
User user = (User) req.getSession().getAttribute("user");
if(user != null){
chain.doFilter(request,response);
}
else res.sendRedirect(req.getContextPath()+"/запрос_учетных_данных.xhtml");
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void destroy() {
}
}

Programmatically control login with Servlet 3.0

I've tested the default security containers in Glassfish 3.0.1 and come to the conclusion that I won't spend any more time on that. Instead I want to control the verification myself. But I need some guidance to get me on right track.
At the moment I have a UserBean that has a login/logout function (see below). And I don't want to use the *j_security_check* built in container, but use core JSF 2.0.
My questions are;
Do I need a ServletFilter to redirect traffic if the user is not logged in (if accessing certain folders)?
How do I store User Pricipals after the user successfully logged in ?
Appreciate any help or link to a example, greetings Chris.
PS. Excuse me for clustering two questions together
#ManagedBean
#SessionScoped
public class UserBean {
private AuthenticateUser authenticateUser;
...
public String login() {
FacesContext context = FacesContext.getCurrentInstance();
HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
JsfUtil.log("Username : " +authenticateUser.getUserName());
JsfUtil.log("Password : " +authenticateUser.getPassword());
AuthenticateUser authRequest = authenticationFacade.find(authenticateUser);
try {
if(!authRequest.equals(authenticateUser))
return "/loginError";
request.login(authenticateUser.getUserName(), authenticateUser.getPassword());
return "";
} catch(ServletException e){
JsfUtil.addErrorMessage(e, "Incorrect username or password, please try again.");
return "/loginError";
}
...
public String logOut() {
String result = "/index?faces-redirect=true";
FacesContext context = FacesContext.getCurrentInstance();
HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
try {
request.logout();
} catch (ServletException e) {
JsfUtil.log("Failed to logout user!" +e.getRootCause().toString());
result = "/loginError?faces-redirect=true";
}
return result;
}
When you want to utilize request.login(), then you should really have configured a Realm in the container which represents the user database. But you seem to have replaced the Realm by some AuthenticationFacade. In this case, the request.login() is not useful for you.
You need to just put the user in the session scope and intercept on that. Here's a kickoff example:
#ManagedBean
#SessionScoped
public class UserManager {
#EJB
private UserService userService;
private String username;
private String password;
private User current;
public String login() {
current = userService.find(username, password);
if (current == null) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Unknown login, try again"));
return null;
} else {
return "userhome?faces-redirect=true";
}
}
public String logout() {
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
return "index?faces-redirect=true";
}
public boolean isLoggedIn() {
return current != null;
}
// Getters/setters (but do NOT provide a setter for current!)
}
When taking authentication in hands like this, then you definitely need a filter to restrict access. When using container managed security you would typically specify it as <url-pattern> of <security-constraint> for this. But without it, you've to take it in your hands. It's good to know that JSF managed beans are keyed by their managed bean name in any scope.
UserManager userManager = ((HttpServletRequest) request).getSession().getAttribute("userManager");
if (userManager == null || !userManager.isLoggedIn()) {
((HttpServletResponse) response).sendRedirect("login.xhtml");
} else {
chain.doFilter(request, response);
}
Map the above filter on the desired URL-pattern.
When you still want to reconsider using container managed authentication, then the following related answers may be useful:
Java EE Login Page Problem (and Configuring Realm in Glassfish)
Performing user authentication in Java EE / JSF using j_security_check
Be aware if you are if you are using JDBC realm security. There are some fixed/expected words in the fields where you configure the realm in the Glassfish admin console.
In the JAAS Context: filed, you have to type: jdbcRealm. This keyword makes the security container use the expected JDBC realm. If you type something else, it won't work.
Here is good example, done by Gordan Jugo; Netbeans/Glassfish JDBC Security Realm

Resources