I have a User class marked as entity which also implements UserDetails. I want to be able to grab some properties of a certain user, and based on their values, to return specific roles in the getAuthorities method. Many of those properties are however lazy-loaded and require a Hibernate transaction.
I tried anything from making the user class #Transactional to making the UserDetailsService and RememberMeService which I use #Transactional. None of those works!
All of my other DAO and Service classes mapped as transactional work (and they are just simple classes - no other annotations besides a #Transactional on top)
UPDATE: This is the overriden getUserDetails in class User
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
GrantedAuthority auth = new GrantedAuthority() {
#Override
public String getAuthority() {
// TODO Auto-generated method stub
return "ROLE_USER";
}
};
ArrayList<GrantedAuthority> result = new ArrayList<GrantedAuthority>();
if (options.size() > 0) {
for (Option o : options) {
result.add(createAuthority(Option.getStringType(o.type)));
}
}
result.add(auth);
return result;
}
The UserDetails is not a Spring bean and thus is Spring is not going to look for #Transactional on it.
I would need to see a stack trace but what I'm assuming is that getAuthorities() is being called by another UserDetailsService method directly, i.e., not going through the transactional proxy.
Inject the PlatformTransactionManager into your UserDetailsService and use a TransactionTemplate in getAuthorities() to wrap your DB accessing code in a transaction.
Related
I was exploring spring security and tried to build a small application wherein I have a an entity name User and a userRepository having a one declared method findByUserName(String userName)
#Entity
#Table(name="user")
class User {
#id
private Long id;
private String userName;
private String password;
}
I have heard that spring security depends on principles and not users.
So we have to have a class which implements UserDetails (provided by spring security).
What's the reason behind this?
Secondly, once we have written all this code we need to configure it into a class which I have done as shown below:
public class AppSecurityConfid extends WebSecurityCongigurerAdapter {
// here we have to autowire the service class which we have made to call the
userRepository and find the user based on userName
#Bean
public DAOAuthenicationProvider authenicationProvider() {
// wherein we create an instance and pass the autowired instance and set the
password encoder and return the instance
}
protected void configurer(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider());
}
}
Things up to here make sense, but why we need Authentication Build Manager in this scheme of things?
I am not an expert but I'd like to say something, maybe it can help:
Spring uses "in the background" a way to retrieve user data for authentication when you activate Spring Security. Of course, this method can be overriden so the developer can change how Spring obtains this data in order to support situations where the data is sparced in different tables, from a file, an API REST query, etc.
The authentication data is structured as a list, where each element corresponds to the data used to authenticate each user. This data is structured as a tuple of 3 elements: String username, String hashedPassword and boolean isAccountActive.
You need to provide a way to obtain this data for each user. You do not need to provide the data explicitly, just the way (method) to obtain it. One way to do it (as you said) is creating a class that implements UserDetailsService, which, for example, forces you to implement UserDetails loadUserByUsername(String email);. In this method you need to provide an instance of a class that implements UserDetails, which corresponds to the UserDetails of the User with the username passed as a parameter. This methods (and similar) are used by Spring Security "in the background" to retrieve the UserDetails of a certain user when is trying to access your web server.
If the Userdetails match with the credentials provided in the request, Spring will allow the request to hit your controllers; else it will throw a HTTP 401. Of course there are other authentication methods, but for the sake of simplicity we understand credentials as user / password with HTTP basic authentication.
So, why do we need a class that implements UserDetails? Because is the contract that a class needs to fulfill if it has to be used for internal authentication in Spring Security. Also to separate from a User class the logic of the business with the logic of the security. Probably creating your own class that extends UserDetails is the best idea, but is not actually necessary. For example if you have your own class to contain the data of a user, you just need to understand how to transform your User instance to UserDetails, so Spring Security can use it transparently and the opposite: how the UserDetails instance can be transformed into one of your users.
For example this is a method to obtain the User instance using the UserDetails instance that is currently authenticated in Spring Boot.
#Service
public class SecurityServiceClass{
#Override
public User getLoggedUser() {
String username = ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
Optional<User> user = this.userService.get().stream().filter(r -> r.getEmail().equals(username)).findFirst();
UserDetails userDetails = ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal());
// TODO: make error in case of null
return user.orElse(new User());
}
}
Here I retrieve the User by retrieving the username from the UserDetails and querying it to the DB to recover the User. I am accessing the DB using a repository class.
Here I do the opposite, transforming a User to a UserDetails by creating a Userdetails instance based on the relevant data of the User. (Note that I use the email as username)
#Service
public class UserServiceClass extends GenericServiceClass<User, UUID> {
#Autowired
public UserServiceClass(UserRepository userRepository) {
super(userRepository);
}
#Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
Optional<User> selected = ((UserRepository) this.genericRepository).getUserByEmail(s);
if (selected.isPresent())
{
// Obtain user by email (username)
User user = selected.get();
// Obtain the roles of this user to construct the instance of UserDetails for SpringBoot Security.
Set<Role> roles = user.getRoles();
return org.springframework.security.core.userdetails.User
.withUsername(s)
.roles(roles.stream().toArray(
(n) -> {
return new String[n];
}
))
.password(user.getHashedPassword())
.build();
}
else
{
throw new UsernameNotFoundException("The user with email " + s + " is not registered in the database");
}
}
Finally, regarding AuthenticationManagerBuilder: This is a method that is used to configure authentication. As far as I know, you can define how your application should obtain the UserDetails. I am not sure if you can provide a method or a lambda to retrieve the triplet for authentication String username, String hashedPassword and boolean isAccountActive. What I do know and did in my application is provide the SQL query used to retrieve the triplet from my DB since I have there all that information. Code:
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationBuilder) throws Exception
{
Session session = this.sessionFactory.getCurrentSession();
session.beginTransaction();
authenticationBuilder.userDetailsService(this.userDetailsService()).passwordEncoder(this.passwordEncoder()).and()
.jdbcAuthentication().dataSource(this.dataSource)
.usersByUsernameQuery("select email, hashed_password as passw, true from user where email = ?")
.authoritiesByUsernameQuery("SELECT user.email, CONCAT(elementpermission.journal_id, '_', elementpermission.authority)\n" +
"FROM user, elementpermission\n" +
"WHERE elementpermission.user = user.uuid \n" +
"AND user.email = ?");
session.getTransaction().commit();
session.close();
}
TL;DR
Spring Security needs instances that fulfill the contract of the interface UserDetails because is the interface that Spring Security uses to obtain the relevant data for authentication.
The authentication manager builder is used to config howto obtain the data used for authentication.
You can check this links if you want better information:
https://www.baeldung.com/spring-security-jdbc-authentication
https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/jdbc.html
jdbcAuthentication() instead of inMemoryAuthentication() doesn't give access - Spring Security and Spring Data JPA
I want to do my own implementation of Bearer-only authentication in Wildfly. In essence, I will do the following steps:
When I receive a request, I will check if it has an Authorization header.
I obtain the token and check against a database (in this case I will be using Redis) for the validity of it.
I obtain the role for that user from the database.
I want to be able to use the #RolesAllowed annotation on my rest services.
How do I go about it? How do I need to modify the Wildfly configuration files? What interfaces do I need to implement? How can I pass the role of the user to the security context so that Wildfly does the #RolesAllowed check for me?
If answering, consider that I am an experienced Java Programmer, but new to Wildfly, so you can skip details on programming logic but not on Wildfly configuration. Also in your answer don't worry on how the token got to Redis in the first place, or how the client obtained it.
EDIT
This is what I have done, but with no luck yet. I have implemented an AuthenticationFilter that implements ContainerRequestFilter. (I am including below only the main filter function that I have implemented. Note that there are some helper functions that get the roles from the database that are not included). Even when, at the end of the function I set the security context of the request context with the user profile (which contains the role), I cannot get to work the #RolesAllowed annotations on my JAX-RS rest services. Any pointers on what should I do?
Note: I have not modified any Wildfly configuration files or the web.xml file. I know that the filter is being called for every request because I am able to LOG messages from it on every request.
/**
* (non-Javadoc)
* #see javax.ws.rs.container.ContainerRequestFilter#filter(javax.ws.rs.container.ContainerRequestContext)
*/
#Override
public void filter(ContainerRequestContext requestContext) {
//1. Read the JSON web token from the header
String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
return;
}
String token = authorizationHeader.substring("Bearer".length()).trim();
try{
//Note that if the token is not in the database,
//an exception will be thrown and we abort.
UserProfile userProfile = this.getUserProfile(token);
if (null == userProfile){
userProfile = this.decodeToken(token);
}
if (null == userProfile){
throw new Exception();
}
String role = userProfile.getUserRole();
if (null == role){
role = this.getRoleFromMod(userProfile);
if (null == role){
role = RoleType.READ_ONLY;
}
userProfile.setUserRole(role);
this.updateUserProfileForToken(token, userProfile);
}
userProfile.setUserRole(role);
//5. Create a security context class that implements the crazy interface
//and set it here.
requestContext.setSecurityContext(new ModSecurityContext(userProfile));
}
catch(Exception e){
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
}
}
Yeah I am not sure how it would work in an EE environment, even making the resource class an stateless bean. The #RolesAllowed annotation is meant to be used for ejbs. In which case the principal is retrieved from the servlet request (I believe). What I would do is just implements your own authorization filter that looks up the annotation and checks against the principal in the security context.
You can see how Jersey implements it. Nothing is really Jersey specific about it except the AnnotatedMethod class. For that you can just do some reflection with java.lang.reflect.Method (resourceInfo.getResourceMethod()) instead. Other than that, you can pretty much copy the code as is. Once you're done, just register the RolesAllowedDynamicFeature with the application. Or just annotate it with #Provider to be scanned for.
You will also need to make sure your authentication filter is annotated with #Priority(Priorities.AUTHENTICATION) so that it is called before the authorization filter, which is annotated with #Priority(Priorities.AUTHORIZATION).
UPDATE
Here is a refactor of the the code I linked to, so It doesn't use an Jersey specific classes. The AnnotatedMethod is just changed to Method.
#Provider
public class RolesAllowedFeature implements DynamicFeature {
#Override
public void configure(ResourceInfo resourceInfo, FeatureContext configuration) {
Method resourceMethod = resourceInfo.getResourceMethod();
if (resourceMethod.isAnnotationPresent(DenyAll.class)) {
configuration.register(new RolesAllowedRequestFilter());
return;
}
RolesAllowed ra = resourceMethod.getAnnotation(RolesAllowed.class);
if (ra != null) {
configuration.register(new RolesAllowedRequestFilter(ra.value()));
return;
}
if (resourceMethod.isAnnotationPresent(PermitAll.class)) {
return;
}
ra = resourceInfo.getResourceClass().getAnnotation(RolesAllowed.class);
if (ra != null) {
configuration.register(new RolesAllowedRequestFilter(ra.value()));
}
}
#Priority(Priorities.AUTHORIZATION) // authorization filter - should go after any authentication filters
private static class RolesAllowedRequestFilter implements ContainerRequestFilter {
private final boolean denyAll;
private final String[] rolesAllowed;
RolesAllowedRequestFilter() {
this.denyAll = true;
this.rolesAllowed = null;
}
RolesAllowedRequestFilter(final String[] rolesAllowed) {
this.denyAll = false;
this.rolesAllowed = (rolesAllowed != null) ? rolesAllowed : new String[]{};
}
#Override
public void filter(final ContainerRequestContext requestContext) throws IOException {
if (!denyAll) {
if (rolesAllowed.length > 0 && !isAuthenticated(requestContext)) {
throw new ForbiddenException("Not Authorized");
}
for (final String role : rolesAllowed) {
if (requestContext.getSecurityContext().isUserInRole(role)) {
return;
}
}
}
throw new ForbiddenException("Not Authorized");
}
private static boolean isAuthenticated(final ContainerRequestContext requestContext) {
return requestContext.getSecurityContext().getUserPrincipal() != null;
}
}
}
First let me explain a bit about how the DynamicFeature works. For that let's first change the context of discussion to your current implementation of your AuthenticationFilter.
Right now it is a filter that is processed for every request. But let's say we introduced a custom #Authenticated annotation
#Target({METHOD, TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface Authenticated{}
We could use this annotation to annotate different methods and classes. To make it so that only the methods and classes annotated are filtered by the filter, we can introduce a DynamicFeature that checks for the annotation, then only register the filter when the annotation is found. For example
#Provider
public class AuthenticationDynamicFeature implements DynamicFeature {
#Override
public void configure(ResourceInfo resourceInfo, FeatureContext configuration) {
if (resourceInfo.getResourceMethod().isAnnotationPresent(Authenticated.class)) {
configuration.register(new AuthenticationFilter());
return;
}
if (resourceInfo.getResourceClass().isAnnotationPresent(Authenticated.class)) {
configuration.register(new AuthenticationFilter());
}
}
}
Once we register this AuthenticationDynamicFeature class, it will make it so that only methods and classes annotated with #Authenticated will be filtered.
Alternatively, this can even be done within the filter. We can get a reference to the ResourceInfo from within the AuthenticationFilter. For example check for the annotation, if is not there, then move on.
#Provider
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
#Context
private ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext context) throws IOException {
boolean hasAnnotation = false;
if (resourceInfo.getResourceMethod().isAnnotationPresent(Authenticated.class)
|| resourceInfo.getResourceClass().isAnnotationPresent(Authenticated.class)) {
hasAnnotation = true;
}
if (!hasAnnotation) return;
// process authentication is annotation is present
This way we could completely forget about the DynamicFeature. It's better to just use the DynamicFeature, I was just giving an example for demonstration purposes.
But that being said, if we look at the first block of code with the RolesAllowedDynamicFeature, you can better understand what is going on. It only registers the filter for methods and classes annotated with #RolesAllowed and #DenyAll. You could even refactor it to have all the annotation logic in the filter instead of the feature. You only have the filter. Just like I did with the AuthenticationFilter example above. Again this would be just for example purposes.
Now as far as the registration of the DynamicFeature, it works the same way as registering any other resource class or provider class (e.g. your authentication filter). So however you register those, just register the RolesAllowedDynamicFeature the same way. There is scanning, where #Path and #Provider annotations are scanned for. If this is what you are current using, then just annotating the feature class with #Provider should register it. For example just having an empty Application subclass will cause scanning to happen
#ApplicationPath("/api")
public class RestApplication extends Application {}
Then there is explicit registration in your Application subclass. For example
#ApplicationPath("/api")
public class RestApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(AuthenticationFilter.class);
classes.add(RolesAllowedFeature.class);
classes.add(SomeResource.class);
return classes;
}
}
Note that when doing this, you disable any scanning that goes on.
So a couple other things to make sure after all the above is clear it still isn't working.
Make sure your current AuthenticationFilter is annotated with #Priority(Priorities.AUTHENTICATION). This is to ensure that your authentication filter is called before the authorization filter. This needs to happen because the authentication filter is what sets the security context, and the authorization filter checks it.
Make sure you are creating the security context correctly. The authorization filter will call the SecurityContext.isUserInRole(role) passing in roles from the #RolesAllowed annotation. So you need to make sure to implements the isUserInRole correctly.
I have read a lot of #Transactional annotation, I saw stackoverflow answers but it does not help me. So I am creating my question.
My case is to save user with unique email. In DB I have user with email xxx#xxx.com, and I am saving user with the same email address. For saving I have to use entityManager.merge() because of this post thymeleaf binding collections it is not important.
First example:
#Controller
public class EmployeeController extends AbstractCrudController {
// rest of code (...)
#RequestMapping(value = urlFragment + "/create", method = RequestMethod.POST)
public String processNewEmployee(Model model, #ModelAttribute("employee") User employee, BindingResult result, HttpServletRequest request) {
prepareUserForm(model);
if (!result.hasErrors()) {
try {
saveEmployee(employee);
model.addAttribute("success", true);
} catch (Exception e) {
model.addAttribute("error", true);
}
}
return "crud/employee/create";
}
#Transactional
public void saveEmployee(User employee) {
entityManager.merge(employee);
}
private void prepareUserForm(Model model) {
HashSet<Position> positions = new HashSet<Position>(positionRepository.findByEnabledTrueOrderByNameAsc());
HashSet<Role> roles = new HashSet<Role>(roleRepository.findAll());
User employee = new User();
model.addAttribute("employee", employee);
model.addAttribute("allPositions", positions);
model.addAttribute("allRoles", roles);
}
}
This code is throwing TransactionRequiredException, I do not know why? It looks like #Transactional annotation did not work, so I moved annotation to processNewEmployee()
Second example:
#Controller
public class EmployeeController extends AbstractCrudController {
// rest of code (...)
#Transactional
#RequestMapping(value = urlFragment + "/create", method = RequestMethod.POST)
public String processNewEmployee(Model model, #ModelAttribute("employee") User employee, BindingResult result, HttpServletRequest request) {
prepareUserForm(model);
if (!result.hasErrors()) {
try {
entityManager.merge(employee);
model.addAttribute("success", true);
} catch (Exception e) {
model.addAttribute("error", true);
}
}
return "crud/employee/create";
}
private void prepareUserForm(Model model) { /*(.....)*/ }
}
And this code is throwing PersistenceException (because of ConstraintViolationException) and of course I got "Transaction marked as rollbackOnly" exeption.
When I try to save email which not exists this code works fine, so I thing that #Transactional annotation is configured well.
If this is important I am putting my TransationManagersConfig:
#Configuration
#EnableTransactionManagement
public class TransactionManagersConfig implements TransactionManagementConfigurer {
#Autowired
private EntityManagerFactory emf;
#Autowired
private DataSource dataSource;
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager tm =
new JpaTransactionManager();
tm.setEntityManagerFactory(emf);
tm.setDataSource(dataSource);
return tm;
}
public PlatformTransactionManager annotationDrivenTransactionManager() {
return transactionManager();
}
}
Could you explain my what I am doing wrong and suggest possible solution of this problem?
Solution:
Thanks to R4J I have created UserService and in my EmployeeController I am using it instead of entityManager.merge() now it works fine
#Service
public class UserService {
#PersistenceContext
private EntityManager entityManager;
#Transactional
public void merge(User user) {
entityManager.merge(user);
}
}
And EmployeeController:
#Controller
public class EmployeeController extends AbstractCrudController {
#Autowired
private UserService userService;
#RequestMapping(value = urlFragment + "/create", method = RequestMethod.POST)
public String processNewEmployee(Model model, #ModelAttribute("employee") User employee, BindingResult result, HttpServletRequest request) {
// (.....)
userService.merge(employee);
// (.....)
}
}
Your transactions don't work because you call directly 'this.saveEmployee(...)' from your 'public String processNewEmployee' method.
How come?
When you add #Transactional, Spring creates a Proxy for your Component and proxies all public methods. So when Spring itself calls your method as a HTTP Rest Request it is considered an external call that goes properly through a Proxy and new Transaction is started as required and code works.
But when you have a Proxied Component and you call 'this.saveEmployee' (that has #Transactional annotation) inside your class code you are actually bypassing the Proxy Spring has created and new Transaction is not started.
Solution:
Extract entire database logic to some sort of a Service or DAO and just Autowire it to your Rest Controller. Then everything should work like a charm.
You should avoid direct database access from Controllers anyway as it is not a very good practice. Controller should be as thin as possible and contain no business logic because it is just a 'way to access' your system. If your entire logic is in the 'domain' then you can add other ways to run business functionalities (like new user creation) in a matter of just few lines of code.
I am using Spring framework and Spring Data JPA to develop an application. Below are one of the repository interface and service class.
public interface UserRepository extends JpaRepository<User, Long>
User findByName(String name);
User findByEmail(String email);
}
public class DefaultUserService implements UserService {
#Inject protected UserRepository userRepo;
#Override
public User getUserById(Long id) {
return userRepo.findOne(id);
}
#Override
public User getUserByName(String name) {
return userRepo.findByName(name);
}
#Override
public User getUserByEmail(String email) {
return userRepo.findByEmail(email);
}
}
As stated by many experts, service layer design should be coarse grained and focused on application operations. Looking at the service class above, I believe that is not a good design as it directly expose all finder methods from the repository. Since all 3 service methods above are returning the same object type (User), I would want to expose only one finder method instead of three that able to encapsulate all finder logic.
public class DefaultUserService implements UserService {
#Inject protected UserRepository userRepo;
// what would be the arguments and logic for this method.
#Override
public User getUser() {
}
}
I appreciate if anyone can point me the solution on how to solve this design issue?
I think the design is not so bad, I mean I was seeing that kind of approach several times, in fact you have several finder methods but each one use different property to obtain the User, if you want to make a service method which encapsulate the logic to retrieve the user I would suggest something like this.
public class DefaultUserService implements UserService {
#Inject protected UserRepository userRepo;
enum UserFindEnum{
ID, EMAIL, NAME;
}
public User getUser(UserFindEnum e, Object obj){
switch(e.ordinal()){
case 0:
return userRepo.findOne(obj);
case 1:
return userRepo.findByName(obj);
case 2:
return userRepo.findByEmail(obj);
default:
break;
}
}
}
I mean you need to know which property you will use to find the User so at least one parameter need to be sent to the service layer so getUser() it is not enough. Probably using some kind of logic as above you will have only one service method and needed logic within it.
In my controllers, when I need the active (logged in) user, I am doing the following to get my UserDetails implementation:
User activeUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.debug(activeUser.getSomeCustomField());
It works fine, but I would think Spring could make life easier in a case like this. Is there a way to have the UserDetails autowired into either the controller or the method?
For example, something like:
public ModelAndView someRequestHandler(Principal principal) { ... }
But instead of getting the UsernamePasswordAuthenticationToken, I get a UserDetails instead?
I'm looking for an elegant solution. Any ideas?
Preamble: Since Spring-Security 3.2 there is a nice annotation #AuthenticationPrincipal described at the end of this answer. This is the best way to go when you use Spring-Security >= 3.2.
When you:
use an older version of Spring-Security,
need to load your custom User Object from the Database by some information (like the login or id) stored in the principal or
want to learn how a HandlerMethodArgumentResolver or WebArgumentResolver can solve this in an elegant way, or just want to an learn the background behind #AuthenticationPrincipal and AuthenticationPrincipalArgumentResolver (because it is based on a HandlerMethodArgumentResolver)
then keep on reading — else just use #AuthenticationPrincipal and thank to Rob Winch (Author of #AuthenticationPrincipal) and Lukas Schmelzeisen (for his answer).
(BTW: My answer is a bit older (January 2012), so it was Lukas Schmelzeisen that come up as the first one with the #AuthenticationPrincipal annotation solution base on Spring Security 3.2.)
Then you can use in your controller
public ModelAndView someRequestHandler(Principal principal) {
User activeUser = (User) ((Authentication) principal).getPrincipal();
...
}
That is ok if you need it once. But if you need it several times its ugly because it pollutes your controller with infrastructure details, that normally should be hidden by the framework.
So what you may really want is to have a controller like this:
public ModelAndView someRequestHandler(#ActiveUser User activeUser) {
...
}
Therefore you only need to implement a WebArgumentResolver. It has a method
Object resolveArgument(MethodParameter methodParameter,
NativeWebRequest webRequest)
throws Exception
That gets the web request (second parameter) and must return the User if its feels responsible for the method argument (the first parameter).
Since Spring 3.1 there is a new concept called HandlerMethodArgumentResolver. If you use Spring 3.1+ then you should use it. (It is described in the next section of this answer))
public class CurrentUserWebArgumentResolver implements WebArgumentResolver{
Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) {
if(methodParameter is for type User && methodParameter is annotated with #ActiveUser) {
Principal principal = webRequest.getUserPrincipal();
return (User) ((Authentication) principal).getPrincipal();
} else {
return WebArgumentResolver.UNRESOLVED;
}
}
}
You need to define the Custom Annotation -- You can skip it if every instance of User should always be taken from the security context, but is never a command object.
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface ActiveUser {}
In the configuration you only need to add this:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"
id="applicationConversionService">
<property name="customArgumentResolver">
<bean class="CurrentUserWebArgumentResolver"/>
</property>
</bean>
#See: Learn to customize Spring MVC #Controller method arguments
It should be noted that if you're using Spring 3.1, they recommend HandlerMethodArgumentResolver over WebArgumentResolver. - see comment by Jay
The same with HandlerMethodArgumentResolver for Spring 3.1+
public class CurrentUserHandlerMethodArgumentResolver
implements HandlerMethodArgumentResolver {
#Override
public boolean supportsParameter(MethodParameter methodParameter) {
return
methodParameter.getParameterAnnotation(ActiveUser.class) != null
&& methodParameter.getParameterType().equals(User.class);
}
#Override
public Object resolveArgument(MethodParameter methodParameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
if (this.supportsParameter(methodParameter)) {
Principal principal = webRequest.getUserPrincipal();
return (User) ((Authentication) principal).getPrincipal();
} else {
return WebArgumentResolver.UNRESOLVED;
}
}
}
In the configuration, you need to add this
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="CurrentUserHandlerMethodArgumentResolver"/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
#See Leveraging the Spring MVC 3.1 HandlerMethodArgumentResolver interface
Spring-Security 3.2 Solution
Spring Security 3.2 (do not confuse with Spring 3.2) has own build in solution: #AuthenticationPrincipal (org.springframework.security.web.bind.annotation.AuthenticationPrincipal) . This is nicely described in Lukas Schmelzeisen`s answer
It is just writing
ModelAndView someRequestHandler(#AuthenticationPrincipal User activeUser) {
...
}
To get this working you need to register the AuthenticationPrincipalArgumentResolver (org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver) : either by "activating" #EnableWebMvcSecurity or by registering this bean within mvc:argument-resolvers - the same way I described it with may Spring 3.1 solution above.
#See Spring Security 3.2 Reference, Chapter 11.2. #AuthenticationPrincipal
Spring-Security 4.0 Solution
It works like the Spring 3.2 solution, but in Spring 4.0 the #AuthenticationPrincipal and AuthenticationPrincipalArgumentResolver was "moved" to an other package:
org.springframework.security.core.annotation.AuthenticationPrincipal
org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver
(But the old classes in its old packges still exists, so do not mix them!)
It is just writing
import org.springframework.security.core.annotation.AuthenticationPrincipal;
ModelAndView someRequestHandler(#AuthenticationPrincipal User activeUser) {
...
}
To get this working you need to register the (org.springframework.security.web.method.annotation.) AuthenticationPrincipalArgumentResolver : either by "activating" #EnableWebMvcSecurity or by registering this bean within mvc:argument-resolvers - the same way I described it with may Spring 3.1 solution above.
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
#See Spring Security 5.0 Reference, Chapter 39.3 #AuthenticationPrincipal
While Ralphs Answer provides an elegant solution, with Spring Security 3.2 you no longer need to implement your own ArgumentResolver.
If you have a UserDetails implementation CustomUser, you can just do this:
#RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(#AuthenticationPrincipal CustomUser customUser) {
// .. find messages for this User and return them...
}
See Spring Security Documentation: #AuthenticationPrincipal
Spring Security is intended to work with other non-Spring frameworks, hence it is not tightly integrated with Spring MVC. Spring Security returns the Authentication object from the HttpServletRequest.getUserPrincipal() method by default so that's what you get as the principal. You can obtain your UserDetails object directly from this by using
UserDetails ud = ((Authentication)principal).getPrincipal()
Note also that the object types may vary depending on the authentication mechanism used (you may not get a UsernamePasswordAuthenticationToken, for example) and the Authentication doesn't strictly have to contain a UserDetails. It can be a string or any other type.
If you don't want to call SecurityContextHolder directly, the most elegant approach (which I would follow) is to inject your own custom security context accessor interface which is customized to match your needs and user object types. Create an interface, with the relevant methods, for example:
interface MySecurityAccessor {
MyUserDetails getCurrentUser();
// Other methods
}
You can then implement this by accessing the SecurityContextHolder in your standard implementation, thus decoupling your code from Spring Security entirely. Then inject this into the controllers which need access to security information or information on the current user.
The other main benefit is that it is easy to make simple implementations with fixed data for testing, without having to worry about populating thread-locals and so on.
Implement the HandlerInterceptor interface, and then inject the UserDetails into each request that has a Model, as follows:
#Component
public class UserInterceptor implements HandlerInterceptor {
....other methods not shown....
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
if(modelAndView != null){
modelAndView.addObject("user", (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal());
}
}
Starting with Spring Security version 3.2, the custom functionality that has been implemented by some of the older answers, exists out of the box in the form of the #AuthenticationPrincipal annotation that is backed by AuthenticationPrincipalArgumentResolver.
An simple example of it's use is:
#Controller
public class MyController {
#RequestMapping("/user/current/show")
public String show(#AuthenticationPrincipal CustomUser customUser) {
// do something with CustomUser
return "view";
}
}
CustomUser needs to be assignable from authentication.getPrincipal()
Here are the corresponding Javadocs of
AuthenticationPrincipal and AuthenticationPrincipalArgumentResolver
#Controller
public abstract class AbstractController {
#ModelAttribute("loggedUser")
public User getLoggedUser() {
return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
}
And if you need authorized user in templates (e.g. JSP) use
<%# taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<sec:authentication property="principal.yourCustomField"/>
together with
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${spring-security.version}</version>
</dependency>
You can try this:
By Using Authentication Object from Spring we can get User details from it in the controller method . Below is the example , by passing Authentication object in the controller method along with argument.Once user is authenticated the details are populated in the Authentication Object.
#GetMapping(value = "/mappingEndPoint") <ReturnType> methodName(Authentication auth) {
String userName = auth.getName();
return <ReturnType>;
}
To get the Active Users Details you can use #AuthenticationPrincipal in your controller like this:-
public String function(#AuthenticationPrincipal UserDetailsImpl user,
Model model){
model.addAttribute("username",user.getName()); //this user object
contains user details
return "";
}
UserDetailsImpl.java
import com.zoom.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
public class UserDetailsImpl implements UserDetails {
#Autowired
private User user;
public UserDetailsImpl(User user) {
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ADMIN");
return List.of(simpleGrantedAuthority);
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getEmail();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
public String getRole(){
return user.getRole();
}
public String getName(){
return user.getUsername();
}
public User getUser(){
return user;
}
}