I'm new at Spring Security. I'm using jsf2 with spring security 3. Three questions:
How can I access, from a session managed bean, the user info (name,password,roles) of the user currently logged in?
In order to use it in a view, for example for rendering elements depending on the roles of the user.
How can I know if a user is logged in? In order to show in a view a "login link" if the user is not logged in, or a "logout link" if the user is logged in. Which property of Spring Security do I have to use in my managed bean to store this info and use it in the view?
The "login link" is just a GET request to the URL of the login page. But how can I show "logout link"? Do it have to be a POST request and use "h:commandLink" like this?:
<h:commandLink value="Logout" action="#{request.contextPath}/j_spring_security_logout" />
Or can it be a GET request?:
<h:link value="Logout" outcome="#{request.contextPath}/j_spring_security_logout" />
Thank you very much in advanced.
The object authentication is who save this properties, you can obtain with next line in your managedBean:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
A user is logged if his Authentication is not a instace of AnonymousAuthenticationToken, in your spring-security-context.xml you must define the urls intercepted by Spring.
The first interceptor is not analyzed by Spring. In this case the Authentication object is an instance of AnonymousAuthenticationToken.
The second interceptor is analyzed by Spring and the user is redirected to login page declared in spring-security-context.xml
/* This is a example for to obtain the rol name for example for generate automatic menu */
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String namePrincipalRol = null;
if (auth instanceof AnonymousAuthenticationToken) {
namePrincipalRol = "ROLE_ANONYMOUS";
} else {
namePrincipalRol = auth.getAuthorities().iterator().next().getAuthority();
}
Good question, I am not sure but I think I remember having read that it must be POST, would be interesting to try. I use h:outputLink
Kind regards.
Related
Since PrimeFaces push is going to be discontinued, I started to migrate to OmniFaces push. For notification purposes it's all working as expected, but I have a chat on my application where I'm missing two things in OmniFaces:
1) Change the user of the socket on demand. I require this for private conversations. On PrimeFaces I create a channel for each conversation based on the logged users id, and I pass it to the connect method of their socket component. With OmniFaces I tried to place the socket component inside a PrimeFaces output panel, bind the user property to a view scoped bean, update the panel on a ajax request and on the oncomplete event call OmniFaces.Push.open('channelName'), but I can see on the SocketObserver class(from the showcase) that the user has not been updated. As a workaround I created the channel using the logged user id as user, and if someone different from the person that he is talking at the moment sends a message, I ignore it at the JavaScript callback instead of appending it.
2) When I don't specify the user on PushContext.send, I'd like that the message was sent to everyone connected on the channel, similar to "/channel/*" on PrimeFaces. My requirement with this is to update the list off online/offline users off this chat application. A a workaround I could create a separate channel for these notifications, but as the application don't require this to work, I preferred to check here first.
I'll add code if necessary, but first I'd like to check if these concepts are present in OmniFaces push.
Thanks
It was indeed not possible to change the <o:socket user="#{...}"> value while staying in the same JSF view (ajax updates, etc). As per issue 472, it has been improved in OmniFaces 3.2-SNAPSHOT.
Any dynamic change in value of <o:socket user="#{...}"> during any ajax request in the same JSF view will now be reflected in push behavior.
In other words, below construct will now be possible:
<h:form>
<h:selectOneMenu value="#{bean.chat}">
<f:selectItems value="#{bean.chats}" />
<f:ajax render="#form" />
<h:selectOneMenu>
...
<o:socket channel="chat" user="#{bean.chat.id}" />
</h:form>
If you start by using the user attribute on the o:socket like in the example
<o:socket channel="sess" scope="session" user="#{pushTestUser}" />
1) seems possible by using the 'user id' as can be seen in the showcase push test page
From http://showcase.omnifaces.org/push/socket:
#Inject #Push
private PushContext someChannel;
public void sendMessage(Object message, User recipientUser) {
Long recipientUserId = recipientUser.getId();
someChannel.send(message, recipientUserId);
}
In this example the pass the User which is an 'example' object that could be your own or whatever. In the showcase the user is passed on by binding the id to an input field, but that could be done server-side as well. Up to you
2) seems possible with sending messages to the generic channel or even groups as can be seen in http://showcase.omnifaces.org/push/socket.
#Inject #Push
private PushContext someChannel;
public void sendMessage(Object message, Group recipientGroup) {
Collection<Long> recipientUserIds = recipientGroup.getUserIds();
someChannel.send(message, recipientUserIds);
}
The group here is an example Object that could be your own, it could be passed from the 'frontend' or just read in the backend somewhere. It just needs to contain id's of users (can be mapped guids, does not need to be internal user id's) that are subscribed to the channel. All are just example methods.
So both seem possible in my opinion.
I have two facelets pages (login.xhtml and user-registration.xhtml). In the login page I have two forms, one for the login and another for the user registration (where I only ask for the email and password twice).
I would like to pass the email and password as attributes from the user registration form to the user-registration.xhtml page (where I ask for the rest of the user registration fields). I don't want to pass them as parameters in the GET url for security reasons.
Can I pass them as attributes while doing a redirect to the user-registration.xhtml page?
you can use this
<p:button outcome="page2" >
<f:param name="nameofData" value="theInformation"></f:param>
</p:button>
I suppose that this composant is in the page1.xhtml and the page1 and page2 are in the same folder (in case note you need to change your outcome) , and for your param you will send theInformation with a name in our case it is nameofData and you will get the informations with nameofData.
Hope that helped you
I´m new at JSF programming and got a problem with my login/session which gets lost after the login.
I want to implement an easy login where a user can type in username and password. So I wrote a LoginController:
#ManagedBean
#SessionScoped
public class LoginController extends AbstractController{
#PostConstruct
public void initialiseSession() {
FacesContext.getCurrentInstance().getExternalContext().getSession(true);
}
private String username = "null";
private String password;
private boolean loggedIn = false;
#Inject
private EmployeeService employeeService;
public static final String employeeSessionKey = "user";
public LoginController() {
}
public String login() {
//check username and password and if true redirect to "/"
}
My login.xhtml looks like:
<h:form id="loginForm">
<h:outputLabel style="font-size:24px" value="Bitte melden Sie sich an!"/>
<p:panelGrid columns="2">
<p:outputLabel id="userOutput" for="userInput" value="Benutzername"/>
<p:inputText id="userInput" value="#{loginController.username}"></p:inputText>
<p:outputLabel id="passwordOutput" for="passwordInput" value="Passwort"/>
<p:inputText id="passwordInput" type="password" value="#{loginController.password}"></p:inputText>
<h:outputText value="Logindaten merken?" id="outputRememberLogin">
<p:selectBooleanCheckbox id="loginCheckbox">
</p:selectBooleanCheckbox>
<p:spacer width="10" id="loginFormLittleSpacer"></p:spacer>
</h:outputText>
<p:commandButton id="loginButton" value="Anmelden" action="#{loginController.login()}" ajax="false" >
</p:commandButton>
</p:panelGrid>
</h:form>
So when I login the redirect works. But when I go to another .xhtml page the session gets lost.
To test this, I put
<p:outputLabel value="#{loginController.username}"/>
on my pages. After the login, the username becomes "null".
I´m going crazy on this problem.
Any ideas?
thanks before.
Your LoginController bean looks like its annotations are okay and the code looks like it should work. However, there's a couple other things that you may want to check. Some of this may be obvious but your question is missing a few details so I’m not sure what level of experience you may have and where to start in this answer. Therefore, I’m starting from the beginning (almost)…
1. Domain Name Configuration
To use sessions, you must use a qualified domain name. Sending a request to an IP address will not allow sessions to work since client browsers only send session information to a fully qualified domain name (http://example.com/). If you’re calling your web app with an IP (such as ‘http://127.0.0.1:8080/MyApp’), the session data will never be sent to your web app and you will have a new session created with each request. Make sure you’re using a fully qualified domain name and path with each request to your application, for example ‘http://localhost:8080/MyApp’.
2. Application Configuration
Check that your web application’s <session-config> configuration is setup correctly. The default config should allow your code to work without having to add anything specific, so if you didn’t add anything, don’t worry about this. However, you may want to make sure there’s nothing that may be preventing the sessions from being reused.
Session cookies should be enabled (<tracking-mode>COOKIE</tracking-mode>)
Timeout should be long enough to not expire before the second request (<session-timeout>60</session-timeout>)
The cookie path should be correctly set for your use (<path>/</path>)
The following is a common session config that I use…
<session-config>
<session-timeout>60</session-timeout>
<tracking-mode>COOKIE</tracking-mode>
<cookie-config>
<path>/</path>
<http-only>true</http-only>
<secure>false</secure>
</cookie-config>
</session-config>
3. Session Cookie Tracking on the Client
If the above points don’t resolve the issue, you can really start diving into the session tracking by monitoring the request and response traffic between your app and the client browser. The session info is passed back and forth by a cookie (or query string parameter if cookies are disabled) named ‘JSESSIONID’. Its value will be the ID of the unique session and must be the same for each request to ensure that your web application tracks the same session. The below highlights some of the things to look for…
Make sure the ‘JSESSIONID’ session cookie is sent to the client browser correctly and is being sent back with each subsequent request. You can do this with Chrome or Safari's web developer tools (under the 'Network' tab) or with a separate utility, such as Wireshark.
Make sure the 'JSESSIONID' cookie exists in the response from the first request… this will let you know that your web app is at least creating the session and response cookie.
Make sure the second request from the client browser is passing the 'JSESSIONID' cookie back... this is the only way your web app knows which existing session to use.
Make sure the 'JESSIONID' cookie has a path of '/' (which may display as 'N/A' in the browser) or the path of your web app ('/MyApp', for example)… the client browser will only send cookies to the domain(s) and path associated with each. For example, if your login page is 'http://example.com/MyApp/login', the 'JSESSIONID' cookie may have a path of '/MyApp' (by default), which will not be returned if the following request is made to 'http://example.com/' (without the '/MyApp' path). The default path is the name of your web app (‘/MyApp’) and can be changed using the <path> config item indicated above.
4. Session Management within the Bean (Additional Information)
In your initialiseSession() method, the session object returned by FacesContext.getCurrentInstance().getExternalContext().getSession(true); isn't being saved and this line is essentially doing nothing. The #SessionScoped annotation will have already created a session and added the ‘JSESSIONID’ cookie to the response before initialiseSession() is called. Therefore, the only reason to call getSession(true) is if you want to save the session to a private object within the bean, which would be like this...
#ManagedBean
#SessionScoped
public class LoginController extends AbstractController{
// Create a global, private member for storing the session data...
private HttpSession session;
#PostConstruct
public void initialiseSession() {
// Assign the session to the global member…
session = FacesContext.getCurrentInstance().getExternalContext().getSession(true);
}
…
Again, the call to the getSession(true) isn’t necessary in your example since the SessionScoped bean will have already created the session. The above code is only necessary if you intended to update or use the session object, for example add an attribute or modify a setting.
In summary
Double-check the above points 1 – 3. My assumption would be that the ‘JSESSIONID’ cookie isn’t being sent back in subsequent requests. If you can confirm that the ‘JSESSIONID’ cookie (with the same value) is being included in each request then the problem isn't related to the session and may be related to the code in your login() method.
i used cookies for repair this kind of problems, in my application. inside Faceutils doesn't work before servlet.
I have implemented web application login using j-security-check which accepts parameters j_username and j_password for authentication with background registry. Now, I need to add date of birth as one more parameter and need to authenticate the user with this parameter too.
Is there a way to extend j-security-check to accept additional parameters?
I would like to avoid performing the check in a servlet filter, if possible.
Using WebSphere V8, MyFaces JSF 2.0, Servlet 3.0, Custom database based authentication
The easiest way would be to append the date of birth to the actual j_username (ie. with JavaScript and then manually split it in the login module.
Replace j_security_check by programmatic login via HttpServletRequest#login().
E.g.
<h:form>
<h:inputText value="#{bean.username}" />
<h:inputSecret value="#{bean.password}" />
<h:inputText value="#{bean.birthdate}" />
<h:commandButton value="Login" action="#{bean.login}" />
</h:form>
with
public void login() {
// Do your thing with birthdate here.
// ...
// Then perform programmatic login.
try {
request.login(username, password);
// Login success. Redirect to landing page.
} catch (ServletException e) {
// Login fail. Return to login page.
}
}
This is in detail outlined in 2nd part of this answer: Performing user authentication in Java EE / JSF using j_security_check
I am developing a web application where there are few roles like Admin,Reporter,Manager,Customer.Agent.Based on Role, some menu item need to be displayed . Admin can give permission (dynamically) to user say Agent ( which is not default permission ).Is there a better way to handle this situation ??
Thanks
You could have some controller which is responsible for permission logic. You would have a permission system where you can grant specific permissions to specific users / groups. The controller could be implemented as a jsf managed bean. You could have a method like this:
public boolean hasPermission(PermissionKey permissionKey) {
...
}
This method would check role + specific permissions.
PermissionKey, in this example, would be an enum, but you can make it a string or something else. Possible values would be, for example, "DELETE_ACCOUNT" or "HANDLE_PAYMENT".
In your views, you can just conditionnaly display components like this:
<h:outputText value="some text" rendered="#{authController.hasPermission('DELETE_ACCOUNT')}" />