I have a Spring MVC web app which uses Spring Security. I want to know the username of the currently logged in user. I'm using the code snippet given below . Is this the accepted way?
I don't like having a call to a static method inside this controller - that defeats the whole purpose of Spring, IMHO. Is there a way to configure the app to have the current SecurityContext, or current Authentication, injected instead?
#RequestMapping(method = RequestMethod.GET)
public ModelAndView showResults(final HttpServletRequest request...) {
final String currentUser = SecurityContextHolder.getContext().getAuthentication().getName();
...
}
If you are using Spring 3, the easiest way is:
#RequestMapping(method = RequestMethod.GET)
public ModelAndView showResults(final HttpServletRequest request, Principal principal) {
final String currentUser = principal.getName();
}
A lot has changed in the Spring world since this question was answered. Spring has simplified getting the current user in a controller. For other beans, Spring has adopted the suggestions of the author and simplified the injection of 'SecurityContextHolder'. More details are in the comments.
This is the solution I've ended up going with. Instead of using SecurityContextHolder in my controller, I want to inject something which uses SecurityContextHolder under the hood but abstracts away that singleton-like class from my code. I've found no way to do this other than rolling my own interface, like so:
public interface SecurityContextFacade {
SecurityContext getContext();
void setContext(SecurityContext securityContext);
}
Now, my controller (or whatever POJO) would look like this:
public class FooController {
private final SecurityContextFacade securityContextFacade;
public FooController(SecurityContextFacade securityContextFacade) {
this.securityContextFacade = securityContextFacade;
}
public void doSomething(){
SecurityContext context = securityContextFacade.getContext();
// do something w/ context
}
}
And, because of the interface being a point of decoupling, unit testing is straightforward. In this example I use Mockito:
public class FooControllerTest {
private FooController controller;
private SecurityContextFacade mockSecurityContextFacade;
private SecurityContext mockSecurityContext;
#Before
public void setUp() throws Exception {
mockSecurityContextFacade = mock(SecurityContextFacade.class);
mockSecurityContext = mock(SecurityContext.class);
stub(mockSecurityContextFacade.getContext()).toReturn(mockSecurityContext);
controller = new FooController(mockSecurityContextFacade);
}
#Test
public void testDoSomething() {
controller.doSomething();
verify(mockSecurityContextFacade).getContext();
}
}
The default implementation of the interface looks like this:
public class SecurityContextHolderFacade implements SecurityContextFacade {
public SecurityContext getContext() {
return SecurityContextHolder.getContext();
}
public void setContext(SecurityContext securityContext) {
SecurityContextHolder.setContext(securityContext);
}
}
And, finally, the production Spring config looks like this:
<bean id="myController" class="com.foo.FooController">
...
<constructor-arg index="1">
<bean class="com.foo.SecurityContextHolderFacade">
</constructor-arg>
</bean>
It seems more than a little silly that Spring, a dependency injection container of all things, has not supplied a way to inject something similar. I understand SecurityContextHolder was inherited from acegi, but still. The thing is, they're so close - if only SecurityContextHolder had a getter to get the underlying SecurityContextHolderStrategy instance (which is an interface), you could inject that. In fact, I even opened a Jira issue to that effect.
One last thing - I've just substantially changed the answer I had here before. Check the history if you're curious but, as a coworker pointed out to me, my previous answer would not work in a multi-threaded environment. The underlying SecurityContextHolderStrategy used by SecurityContextHolder is, by default, an instance of ThreadLocalSecurityContextHolderStrategy, which stores SecurityContexts in a ThreadLocal. Therefore, it is not necessarily a good idea to inject the SecurityContext directly into a bean at initialization time - it may need to be retrieved from the ThreadLocal each time, in a multi-threaded environment, so the correct one is retrieved.
I agree that having to query the SecurityContext for the current user stinks, it seems a very un-Spring way to handle this problem.
I wrote a static "helper" class to deal with this problem; it's dirty in that it's a global and static method, but I figured this way if we change anything related to Security, at least I only have to change the details in one place:
/**
* Returns the domain User object for the currently logged in user, or null
* if no User is logged in.
*
* #return User object for the currently logged in user, or null if no User
* is logged in.
*/
public static User getCurrentUser() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal()
if (principal instanceof MyUserDetails) return ((MyUserDetails) principal).getUser();
// principal object is either null or represents anonymous user -
// neither of which our domain User object can represent - so return null
return null;
}
/**
* Utility method to determine if the current user is logged in /
* authenticated.
* <p>
* Equivalent of calling:
* <p>
* <code>getCurrentUser() != null</code>
*
* #return if user is logged in
*/
public static boolean isLoggedIn() {
return getCurrentUser() != null;
}
To make it just show up in your JSP pages, you can use the Spring Security Tag Lib:
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/taglibs.html
To use any of the tags, you must have the security taglib declared in your JSP:
<%# taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
Then in a jsp page do something like this:
<security:authorize access="isAuthenticated()">
logged in as <security:authentication property="principal.username" />
</security:authorize>
<security:authorize access="! isAuthenticated()">
not logged in
</security:authorize>
NOTE: As mentioned in the comments by #SBerg413, you'll need to add
use-expressions="true"
to the "http" tag in the security.xml config for this to work.
If you are using Spring Security ver >= 3.2, you can use the #AuthenticationPrincipal annotation:
#RequestMapping(method = RequestMethod.GET)
public ModelAndView showResults(#AuthenticationPrincipal CustomUser currentUser, HttpServletRequest request) {
String currentUsername = currentUser.getUsername();
// ...
}
Here, CustomUser is a custom object that implements UserDetails that is returned by a custom UserDetailsService.
More information can be found in the #AuthenticationPrincipal chapter of the Spring Security reference docs.
I get authenticated user by
HttpServletRequest.getUserPrincipal();
Example:
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.support.RequestContext;
import foo.Form;
#Controller
#RequestMapping(value="/welcome")
public class IndexController {
#RequestMapping(method=RequestMethod.GET)
public String getCreateForm(Model model, HttpServletRequest request) {
if(request.getUserPrincipal() != null) {
String loginName = request.getUserPrincipal().getName();
System.out.println("loginName : " + loginName );
}
model.addAttribute("form", new Form());
return "welcome";
}
}
In Spring 3+ you have have following options.
Option 1 :
#RequestMapping(method = RequestMethod.GET)
public String currentUserNameByPrincipal(Principal principal) {
return principal.getName();
}
Option 2 :
#RequestMapping(method = RequestMethod.GET)
public String currentUserNameByAuthentication(Authentication authentication) {
return authentication.getName();
}
Option 3:
#RequestMapping(method = RequestMethod.GET)
public String currentUserByHTTPRequest(HttpServletRequest request) {
return request.getUserPrincipal().getName();
}
Option 4 : Fancy one : Check this out for more details
public ModelAndView someRequestHandler(#ActiveUser User activeUser) {
...
}
I would just do this:
request.getRemoteUser();
Yes, statics are generally bad - generally, but in this case, the static is the most secure code you can write. Since the security context associates a Principal with the currently running thread, the most secure code would access the static from the thread as directly as possible. Hiding the access behind a wrapper class that is injected provides an attacker with more points to attack. They wouldn't need access to the code (which they would have a hard time changing if the jar was signed), they just need a way to override the configuration, which can be done at runtime or slipping some XML onto the classpath. Even using annotation injection in the signed code would be overridable with external XML. Such XML could inject the running system with a rogue principal. This is probably why Spring is doing something so un-Spring-like in this case.
For the last Spring MVC app I wrote, I didn't inject the SecurityContext holder, but I did have a base controller that I had two utility methods related to this ... isAuthenticated() & getUsername(). Internally they do the static method call you described.
At least then it's only in once place if you need to later refactor.
You could use Spring AOP aproach.
For example if you have some service, that needs to know current principal. You could introduce custom annotation i.e. #Principal , which indicate that this Service should be principal dependent.
public class SomeService {
private String principal;
#Principal
public setPrincipal(String principal){
this.principal=principal;
}
}
Then in your advice, which I think needs to extend MethodBeforeAdvice, check that particular service has #Principal annotation and inject Principal name, or set it to 'ANONYMOUS' instead.
The only problem is that even after authenticating with Spring Security, the user/principal bean doesn't exist in the container, so dependency-injecting it will be difficult. Before we used Spring Security we would create a session-scoped bean that had the current Principal, inject that into an "AuthService" and then inject that Service into most of the other services in the Application. So those Services would simply call authService.getCurrentUser() to get the object. If you have a place in your code where you get a reference to the same Principal in the session, you can simply set it as a property on your session-scoped bean.
The best solution if you are using Spring 3 and need the authenticated principal in your controller is to do something like this:
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
#Controller
public class KnoteController {
#RequestMapping(method = RequestMethod.GET)
public java.lang.String list(Model uiModel, UsernamePasswordAuthenticationToken authToken) {
if (authToken instanceof UsernamePasswordAuthenticationToken) {
user = (User) authToken.getPrincipal();
}
...
}
Try this
Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();
String userName = authentication.getName();
I am using the #AuthenticationPrincipal annotation in #Controller classes as well as in #ControllerAdvicer annotated ones. Ex.:
#ControllerAdvice
public class ControllerAdvicer
{
private static final Logger LOGGER = LoggerFactory.getLogger(ControllerAdvicer.class);
#ModelAttribute("userActive")
public UserActive currentUser(#AuthenticationPrincipal UserActive currentUser)
{
return currentUser;
}
}
Where UserActive is the class i use for logged users services, and extends from org.springframework.security.core.userdetails.User. Something like:
public class UserActive extends org.springframework.security.core.userdetails.User
{
private final User user;
public UserActive(User user)
{
super(user.getUsername(), user.getPasswordHash(), user.getGrantedAuthorities());
this.user = user;
}
//More functions
}
Really easy.
Define Principal as a dependency in your controller method and spring will inject the current authenticated user in your method at invocation.
I like to share my way of supporting user details on freemarker page.
Everything is very simple and working perfectly!
You just have to place Authentication rerequest on default-target-url (page after form-login)
This is my Controler method for that page:
#RequestMapping(value = "/monitoring", method = RequestMethod.GET)
public ModelAndView getMonitoringPage(Model model, final HttpServletRequest request) {
showRequestLog("monitoring");
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String userName = authentication.getName();
//create a new session
HttpSession session = request.getSession(true);
session.setAttribute("username", userName);
return new ModelAndView(catalogPath + "monitoring");
}
And this is my ftl code:
<#security.authorize ifAnyGranted="ROLE_ADMIN, ROLE_USER">
<p style="padding-right: 20px;">Logged in as ${username!"Anonymous" }</p>
</#security.authorize>
And that's it, username will appear on every page after authorisation.
I have many pages, where i want to use a single property value of type String.
Fro example, i have login page, after i login i will set a property, whether it is admin or not, according to this property value, i will do some action on other jsp pages as well. To do this below url is giving some solution, but i don't know how can i use this
http://stackoverflow.com/questions/18211075/how-to-set-controller-wide-global-variables
I want to read this property value and use this in all different jsp pages.
Iadded the following class
public class PagePopulationInterceptor extends HandlerInterceptorAdapter {
#Autowired
private UserService userService;
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
if(modelAndView != null) {
userService.setUserType("admin");
modelAndView.addObject("myUserProfile", userService);
}
}
}
and as well changed spring-servlet xml
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<beans:bean class="com.test.PagePopulationInterceptor" />
</mvc:interceptor>
<!-- other interceptors (locale, theme and so on) -->
</mvc:interceptors>
In JSP how i tried calling is
<div>
<p>${myUserProfile.userType}</p>
</div>
In the above jsp code, i did not receive any values.
Java 1.7
Spring 3.1.1 with Spring-WS 2.1.1
Joda
Hibernate 3.6
MySQL 5.0.57
Maven 3
Tomcat 7
Eclipse 3.7
So the SOAP web service is up and running.
And my standalone Java app can access it.
Now I'm trying to build a Spring MVC web client to access it. So by my way of thinking I can just replace the standard webapp's service layer with the web service endpoint .
Patterning after this
http://ankeetmaini.wordpress.com/2013/03/04/jax-ws-client-consuming-the-jax-ws-webservice
after switching his naming conventions to be more canonical I wound up with
FormsEndpointImplService
#WebServiceClient(name = "FormsEndpointImplService",
wsdlLocation = "http://localhost:8080/dept_forms_webservice/formsService?wsdl",
targetNamespace = "http://endpoint.web.forms.azdeq.gov/")
public class FormsEndpointImplService extends Service
{
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName("http://endpoint.web.forms.azdeq.gov/", "FormsEndpointImplService");
public final static QName FormsEndpointImplPort = new QName("http://endpoint.web.forms.azdeq.gov/", "FormsEndpointImplPort");
static {
URL url = null;
try {
url = new URL("http://localhost:8080/dept_forms_webservice/formsService?wsdl");
} catch (MalformedURLException e) {
java.util.logging.Logger.getLogger(FormsEndpointImplService.class.getName())
.log(java.util.logging.Level.INFO,
"Can not initialize the default wsdl from {0}", "http://localhost:8080/dept_forms_webservice/formsService?wsdl");
}
WSDL_LOCATION = url;
}
public FormsEndpointImplService(URL wsdlLocation) {
super(wsdlLocation, SERVICE);
}
public FormsEndpointImplService(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
public FormsEndpointImplService() {
super(WSDL_LOCATION, SERVICE);
}
//This constructor requires JAX-WS API 2.2. You will need to endorse the 2.2
//API jar or re-run wsdl2java with "-frontend jaxws21" to generate JAX-WS 2.1
//compliant code instead.
public FormsEndpointImplService(WebServiceFeature ... features) {
super(WSDL_LOCATION, SERVICE, features);
}
//This constructor requires JAX-WS API 2.2. You will need to endorse the 2.2
//API jar or re-run wsdl2java with "-frontend jaxws21" to generate JAX-WS 2.1
//compliant code instead.
public FormsEndpointImplService(URL wsdlLocation, WebServiceFeature ... features) {
super(wsdlLocation, SERVICE, features);
}
//This constructor requires JAX-WS API 2.2. You will need to endorse the 2.2
//API jar or re-run wsdl2java with "-frontend jaxws21" to generate JAX-WS 2.1
//compliant code instead.
public FormsEndpointImplService(URL wsdlLocation, QName serviceName, WebServiceFeature ... features) {
super(wsdlLocation, serviceName, features);
}
/**
*
* #return
* returns FormsEndpoint
*/
#WebEndpoint(name = "FormsEndpointImplPort")
public FormsEndpoint getFormsEndpointImplPort() {
return super.getPort(FormsEndpointImplPort, FormsEndpoint.class);
}
/**
*
* #param features
* A list of {#link javax.xml.ws.WebServiceFeature} to configure on the proxy. Supported features not in the <code>features</code> parameter will have their default values.
* #return
* returns FormsEndpoint
*/
#WebEndpoint(name = "FormsEndpointImplPort")
public FormsEndpoint getFormsEndpointImplPort(WebServiceFeature... features) {
return super.getPort(FormsEndpointImplPort, FormsEndpoint.class, features);
}
}
FormsEndpoint ( the SEI )
#WebService(targetNamespace = "http://endpoint.web.forms.azdeq.gov/", name = "FormsEndpoint")
#XmlSeeAlso({ObjectFactory.class})
public interface FormsEndpoint
{
#RequestWrapper(localName = "insertCompletedForm", targetNamespace = "http://endpoint.web.forms.azdeq.gov/", className = "gov.azdeq.forms.web.endpoint.InsertCompletedForm")
#WebMethod
#ResponseWrapper(localName = "insertCompletedFormResponse", targetNamespace = "http://endpoint.web.forms.azdeq.gov/", className = "gov.azdeq.forms.web.endpoint.InsertCompletedFormResponse")
public void insertCompletedForm(
#WebParam(name = "arg0", targetNamespace = "")
gov.azdeq.forms.web.endpoint.CompletedForm arg0
);
....
}
and my servlet-context.xml
...
<bean id="formsWebServiceProxy" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
<property name="wsdlDocumentUrl" value="http://localhost:8080/dept_forms_webservice/formsService?wsdl"/>
<property name="serviceInterface" value="gov.azdeq.forms.web.endpoint.FormsEndpoint"/>
<property name="serviceName" value="FormsEndpointImplService"/>
<property name="portName" value="FormsEndpointImplPort"/>
<property name="namespaceUri" value="http://endpoint.web.forms.azdeq.gov/"/>
<!-- <property name="endpointAddress" value="http://endpoint.web.forms.azdeq.gov/" /> -->
</bean>
<bean id="baseController" class="gov.azdeq.forms.web.controller.BaseController">
<property name="service" ref="formsWebServiceProxy" />
</bean>
...
and Base Controller
#RequestMapping
#Controller
public class BaseController
{
private static final Logger logger = Logger.getLogger( BaseController.class);
#Resource
FormsEndpoint service; // should come in from bean
...
}
So this all compiles and deploys to Tomcat just fine.
But when I click on dept_forms_webclient in localhost:8080/manager it throws this:
org.springframework.beans.NotWritablePropertyException: Invalid property 'service' of bean class [gov.azdeq.forms.web.controller.BaseController]: Bean property 'service' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
Seems like the proxy bean is being injected ok, it seems like I should not be setting it to FormsEndpoint regardless of what various tuts say.
So, more configuration confusion, can anyone spot what's wrong here?
TIA,
Still-learning Stev
I got it all working by losing the dependency-injection in the context file and using #Autowired in the controller like so:
<!-- controllers -->
<bean id="baseController" class="gov.azdeq.forms.web.controller.BaseController" />
and
#RequestMapping
#Controller("baseController")
public class BaseController
{
#Autowired
#Resource(name="formsWebServicePortProxy")
FormsEndpoint formsWebServicePortProxy;
Not sure why using #Autowired works but manual property injection does not. Huh.
Thanks to all who replied, it did give me a bit of insight.
CASE CLOSED
Still-learning Steve
OK, so we are learning Spring MVC 3. And we are a little new to IoC, DI, etc. We want to clean up a lot of our old legacy mistakes. :-)
We are really liking #Autowired for our user services, etc.
However, we now have an issue that we would like to solve with Autowiring.
Let's say we have a Login bean:
public class Login {
private String username;
private String email;
// getters/setters....
}
This bean should be used session wide. We want every controller to be able to access this single object.
Which I'm assuming we need in our application-config.xml
<bean id="login" class="com.example.models.Login" scope="session" />
Also, let's say we have another class as:
public class Employee {
private String firstName;
private String lastName;
private Login login;
public Employee(Login paLogin) {
this.login = paLogin;
}
}
And to put that in session:
<bean id="employee" class="com.example.models.Employee" scope="session" />
OK, later on in our application, we have an email notification service. That service needs to access the username and email from the Login bean AND information from the Employee bean. Granted I could access the login bean from session memory but this is just an example.
#Controller
public class EmailController {
#Autowired
Login login; // this should come from session memory automatically right??
#Autowired
Employee employee; // OK, this should also come from session memory. Which contains a reference of the login too. Correct?
// getters/setters....
public void sendEmails() {
// ....
String email = login.getEmail();
String firstName = employee.getFirstName();
// ....
}
}
I hope this makes sense. What we are really wanting to accomplish is reducing XML configs, reducing constant parameter passing, minimal annotations, etc.
Any help that could point me in the right direction would be appreciated.
Thanks!
Couple of things about the controller you have put up.
#Controller
public class EmailController {
#Autowired
Login login; // The container has to create a bean of type Login to autowire into EmailController
#Autowired
Employee employee; //same as above
// getters/setters....
}
If the container has to create a singleton bean on application startup, you have to mark the Login and Employee class with annotation #Component.
Even annotations like #Repository, #Service does this. You can have look at this answer for difference between these annotations.
So once you mark your classes with any of these annotations, singleton beans of respective type will be created on application startup. You will see something like this in your logs
Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory#6c811e18
And then a list of beans it has created. You can inject these beans to other beans. And these beans aren't stored in the session. But they are managed by the container itself.
You can do away with the xml bean definitions if your are using annotations like #Controller, #Component etc. Also you can avoid majority of your xml configuration files by using #Configuration. You can check here and here for examples.