I would like to see a simple Login Application, not as simple as this though.
What I would like to achieve is an understanding on how JSF works, I've developed a lot of ASP.NET where you have the code behind and where you can just check if a session was created upon Login.
A similar solution in JSF would be great.
This is basically what I want to achieve:
(The "success" and failure are mapped to faces-config.xml)
At the success-page I want to be Certain that the user is logged in, so one should Not be able to navigate to "success.jspx" if you have not got the correct session.
There is no inherent authentication functionality in core JSF beyond being able to use things like component rendered
attributes geared towards role-based security.
By default, a JSF application relies on the same container-managed security mechanisms as the web component that contains it (JEE5 tutorial). 3rd party frameworks like Seam can provide alternatives.
If you want to add your own application security, a servlet filter is one of the simpler mechanisms.
This filter protects resources under the restricted
directory as defined in web.xml
:
<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>restricted.AuthenticationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<url-pattern>/restricted/*</url-pattern>
</filter-mapping>
The filter class implementation:
public class AuthenticationFilter implements Filter {
private FilterConfig config;
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
if (((HttpServletRequest) req).getSession().getAttribute(
AuthenticationBean.AUTH_KEY) == null) {
((HttpServletResponse) resp).sendRedirect("../restricted_login.faces");
} else {
chain.doFilter(req, resp);
}
}
public void init(FilterConfig config) throws ServletException {
this.config = config;
}
public void destroy() {
config = null;
}
}
A login bean defined in faces-config.xml
:
public class AuthenticationBean {
public static final String AUTH_KEY = "app.user.name";
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public boolean isLoggedIn() {
return FacesContext.getCurrentInstance().getExternalContext()
.getSessionMap().get(AUTH_KEY) != null;
}
public String login() {
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(
AUTH_KEY, name);
return "secret";
}
public String logout() {
FacesContext.getCurrentInstance().getExternalContext().getSessionMap()
.remove(AUTH_KEY);
return null;
}
}
The JSF login form in the restricted_login.jsp
page:
<f:view>
<p><a href="restricted/secret.faces">try to go to secret
page</a></p>
<h:form>
Username:
<h:panelGroup rendered="#{not authenticationBean.loggedIn}">
<h:inputText value="#{authenticationBean.name}" />
<h:commandButton value="login"
action="#{authenticationBean.login}" />
</h:panelGroup>
<h:commandButton value="logout"
action="#{authenticationBean.logout}"
rendered="#{authenticationBean.loggedIn}" />
</h:form>
</f:view>
(The redirect URL/mechanism was chosen for brevity rather than any sort of best practice; see the Servlet API for more options.)
If you're willing to try a bit more advanced approach then I suggest looking into spring-security+JSF. It works like a charm.
You can write your application as if it wasn't under security and then just configure which areas that should be protected using aspects.
Spring security: http://static.springsource.org/spring-security/site/
A Tutorial: http://ocpsoft.com/java/acegi-spring-security-jsf-login-page/
The best way to do it would be to use container managed security.
Here is a tutorial on how to achieve that with glassfish
and jsf
.
If you use templates, I've found you don't really need a filter.
index.jsp
<jsp:forward page="startup.faces"></jsp:forward>
startup.xhtml (.faces), doesn't actualy try to show a screen, it calles the javascript startupSubmit() on load and that clicks the button. This sends the flow straight to the method start() in StartupBean.java.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
.
.
<script type="text/javascript">
function startupSubmit() {
**document.getElementById('startupForm:startupBtn').click();**
}
</script>
<h:body onload="startupSubmit()">
<h:form id="startupForm">
<p:commandButton id="startupBtn" value="" action="#{startupBean.start}" ajax="false" />
</h:form>
</h:body>
</html>
StartupBean.java (is not part of the template.xhtml below). The start() method in StartupBean sets a variable called authorized to true (it defaults to false), then jumps to first.xhtml. You can use any criteria you desire to determine if authorized is set to true... such as login criteria.
package gov.irs.eservices.managementBeans;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
@ManagedBean(name="startupBean")
@SessionScoped
public class StartupBean {
private boolean authorized;
public StartupBean() {
}
public String start() {
**setAuthorized(true);**
return "first";
}
public boolean isAuthorized() {
return authorized;
}
public void setAuthorized(boolean authorized) {
this.authorized = authorized;
}
}
template.xhtml. In template.xhtml, just inside the form, you place a h: or p: panelGrid and only render it if startupBean.authorized is true. The only way a user can get to the pages contained in the template is if they come through StartupBean.java first.
<f:view>
<div id="container">
<h:form id="templateForm">
**<p:panelGrid rendered="#{startupBean.authorized}">**
<div id="header">
<ui:include src="header.xhtml" />
</div>
<div id="wrapper">
<div id="firstId">
<ui:insert name="first"></ui:insert>
</div>
.
. <!-- MORE PAGES -->
.
.
</div>
<div id="footer">
<ui:include src="footer.xhtml" />
</div>
</p:panelGrid>
</h:form>
</div>
</f:view>
So, that's my solution. I've tested it pretty thoroughly and it seems to work fine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With