I've been having some trouble figuring this one out.
I've got an multitenant system where users are organized into organizations. Within that organization the usernames must be unique. Otherwise, two organizations can have the same user name.
I've got spring security hooked up with the jdbc-user-service and all that works fine. My problems start when I'm trying to get the current user.
I took a look at a link that leverages spring 3 and the Principal object as a method parameter. This works great, except a Principal doesn't have enough information! First, usernames aren't unique in my usecase, also having easy access to the organization that the user belongs to would be great.
A little more searching dug up this awesome answer. The problem with this is the same problem as before. It relies on the Principal object, which just doesn't have enough info.
(here's the magic)
#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;
}
}
Is there a good way to override the User object to use my own? Am i stuck writing a custom UserDetailService? Is there a better approach than continuing to walk down this path?
Thanks
Make your User object inherit UserDetails(or use a wrapper) and use it as principal.
e.g.
public class MyCustomUser implements UserDetails {
// ..
}
Make a custom UserDetailsService that returns your User object:
#Service
public class MyCustomUserDetailsServiceImpl implements UserDetailsService {
#Autowired
private MyCustomUserDAO userDAO;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userDAO.getByUsername(username);
}
}
Now you can extract your user:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Object principal = auth.getPrincipal();
MyCustomUser user = (MyCustomUser)principal;
user.myCustomMethod();
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
This is my first Question ever here on SO, it was helpfull and saved me lots of time, but now I can't find any solution to my problem.
As I'm rather new to spring and espacially to spring-security, I'm stuck with something that might be easy if i had more knowledge.
I have an existing Application that uses a local user database. It uses a custom UserDetails implementation that works if used with user:password authentification through a login form.
Here is the current setup:
public class SecurityContext extends WebSecurityConfigurerAdapter {
....
#Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider()).userDetailsService(userDetailsService());
}
#Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider result = new DaoAuthenticationProvider();
result.setUserDetailsService(userDetailsService());
result.setPasswordEncoder(passwordEncoder());
return result;
}
#Override
#Bean
public GatesUserDetailsService userDetailsService() {
GatesUserDetailsService result = new GatesUserDetailsService();
result.setClientService(clientService);
result.setAccountService(accountService);
result.setCardService(cardService);
result.setPersonService(personService);
result.setAccountPropertyService(accountPropertyService);
result.setLoginAttemptService(loginAttemptService);
return result;
}
Now I want to use SSO from an external IDP that speaks OpenIdConnect.
Going through the documentation I was able to get this up and running in a "default" manner. That is, at the and of my process a get a user that is an Instance of OidcUser. I need that user to be either extended or incorporate the existing userDetails.
The documentation (Spring Boot and OAuth2) recommends to
Implement and expose OAuth2UserService to call the Authorization
Server as well as your database. Your implementation can delegate to
the default implementation, which will do the heavy lifting of calling
the Authorization Server. Your implementation should return something
that extends your custom User object and implements OAuth2User.
I was able to introduce my own Oauth2UserService that gets called right at the and of the authentification by setting:
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.and()
.oauth2Login()
.failureHandler(authenticationFailureHandler())
.successHandler(authenticationSuccessHandler())
.userInfoEndpoint()
.userService(this.oauth2UserService())
.oidcUserService(this.oidcUserService());}
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
final OidcUserService delegate = new OidcUserService();
return (userRequest) -> {
OidcUser oidcUser = delegate.loadUser(userRequest);
//..DO some additional Stuff check against external Server
//Here I could load my custom userDetails
GatesUserDetails userDetails = (GatesUserDetails) userDetailsService.loadUserByUsername("131:" + username);
....
But I have now Idea how to make my customUser a vaild return to my function.
I tried to implement the OidcUser Interface in my userDetails, but still it does not work.
Any hint (even to a more understandable doc) would be highly appreciated.
EDIT
To clarify things, I implemented the oidcUser Interface as stated in the docs along with the necessary implementations (getAttribute, getAttributes, getAuthorities) but still I could not use this as the return type would still be our GatesUserDetails, no way (for me) to cast it to oidcUser
Have the same problem with spring-security-oauth2-client-5.6.2, after hours google and debugger it solved.
First, make sure your UserInfo entrypoint is correct in case you own the
Auth server.
Plus requested scopes contains any of profiles not
only openid.
Logic found here: OidcUserService::shouldRetrieveUserInfo
private boolean shouldRetrieveUserInfo(OidcUserRequest userRequest) {
// Auto-disabled if UserInfo Endpoint URI is not provided
ProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails();
if (StringUtils.isEmpty(providerDetails.getUserInfoEndpoint().getUri())) {
return false;
}
// The Claims requested by the profile, email, address, and phone scope values
// are returned from the UserInfo Endpoint (as described in Section 5.3.2),
// when a response_type value is used that results in an Access Token being
// issued.
// However, when no Access Token is issued, which is the case for the
// response_type=id_token,
// the resulting Claims are returned in the ID Token.
// The Authorization Code Grant Flow, which is response_type=code, results in an
// Access Token being issued.
if (AuthorizationGrantType.AUTHORIZATION_CODE
.equals(userRequest.getClientRegistration().getAuthorizationGrantType())) {
// Return true if there is at least one match between the authorized scope(s)
// and accessible scope(s)
return this.accessibleScopes.isEmpty()
|| CollectionUtils.containsAny(userRequest.getAccessToken().getScopes(), this.accessibleScopes);
}
return false;
}
Hope this could help someone.
Based on my understanding, there are a number of different ways to retrieve the authenticated username in Spring Security.
I'm currently grabbing the username by included the Principal as a controller method argument:
#RequestMapping(value = "/dashboard", method = RequestMethod.GET)
public ModelAndView displayHomePage(ModelAndView modelAndView, Principal principal) {
modelAndView.addObject("email", principal.getName());
// Render template located at src/main/resources/templates/dashboard.html
modelAndView.setViewName("dashboard");
return modelAndView;
}
Does Spring Security offer an easy way for me to store the User object into the session so it can be easily retrieved by any controller method?
I want to avoid performing a DB lookup each time:
// Lookup user in database by e-mail
User user = userService.findUserByEmail(principal.getName());
I'm using Spring Security 4.2.
Spring Security provides you with a static method for quickly and easy access:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String name = auth.getName();
Or
User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String name = user.getUsername();
Maybe you would like do this in a base abstract class
public abstract class BaseController {
protected User getCurrentUser() {
return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
}
...
public YourController extends BaseController {
...
}
Update
If you want to store the current authenticated user in session, then you need store only first time in a object as suggested by #gkatzioura.
#Component
#Scope("session")
public class MySessionInfo {
private User user;
protected User getCurrentUser() {
if (user == null) {
user = userService.findUserByEmail(SecurityContextHolder.getContext().getAuthentication().getPrincipal().getName());
}
return user;
}
}
You can inject this bean in yours controllers like
#Autowired
private MySessionInfo mySessionInfo;
You must take care about cases when user is not logged, but this is another problem.
You can always use the methods that spring security provides to get basic information such as name, authorities and everything provided by the Authentication.class.
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
authentication.getAuthorities();
authentication.getName();
But if you want more information, using a session bean to store the information is also a good idea.
#Component
#Scope("session")
public class UserInfo { .. }
In JSP I can get username by ${pageContext.request.remoteUser}. But there is also additional info (rating of user) I need to display on every page of my site. How can I access it, considering there is a #Service to get it by username?
For what it's worth I use custom authentication provider:
#Service
public class MyUserDetailsService implements UserDetailsService {
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
return new User(s, "password", Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
}
}
<security:authentication-manager>
<security:authentication-provider user-service-ref='myUserDetailsService'/>
</security:authentication-manager>
You could create implementation of AuthenticationSuccessHandler and set an attribute there:
#Component
public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
request.getSession().setAttribute("someDetail", "detailsValue");
response.sendRedirect("/to-whatever-url-you-want")
}
}
Upon successful login, someDetail attribute will be set. Note that you can also obtain currently logged in user from Authentication instance and perform some logic.
You can create a custom UserDetails class (e.g. MyUserDetails) and save the extra information there. In your UserDetailsService, just return this MyUserDetails instead of the normal UserDetail.
public class MyUserDetails extends UserDetail {
private int rating;
... // other properties
... // getter setter
}
#Service
public class MyUserDetailsService implements UserDetailsService {
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
return new MyUserDetails(...);
}
}
In every controller, you can call
(MyUserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
to the get the current principal/UserDetails, which contains your extra info(e.g. rating of the user).
P.s. If this extra info is related to users, sessions are not the right place to store it, because sessions may expire after closing the browser. If this extra info is just some temporary data, then #Branislav Lazic's answer is correct. Since I can't add a comment, so I have to write the comments to #Branislav Lazic's answer here.
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;
}
}