How to Find Out the Currently Logged-In User in Spring Boot

How to find out the currently logged-in user in Spring Boot?

As per request:

Spring Boot which uses Spring Security internally provides a SecurityContextHolder class which allows the lookup of the currently authenticated user via:

Authentication auth = SecurityContextHolder.getContext().getAuthentication();

The authentication instance now provides the following methods:

  • Get the username of the logged in user: getPrincipal()
  • Get the password of the authenticated user: getCredentials()
  • Get the assigned roles of the authenticated user: getAuthorities()
  • Get further details of the authenticated user: getDetails()

How to get current user in every request in Spring Boot?

With Jhovanni's suggestion, I created an AOP annotation like this:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogUsername {
}

In the same package, I added new @Aop @Component class with AuthenticationFacade injection:

@Aspect
@Component
public class LogUsernameAop {
Logger logger = LoggerFactory.getLogger(LogUsernameAop.class);
@Autowired
private AuthenticationFacade authenticationFacade;

@Before("@annotation(LogUsername)")
public void logUsername() throws Throwable {
logger.info(authenticationFacade.getAuthentication().getName());
LoggedUser.logIn(authenticationFacade.getAuthentication().getName());
}
}

Then, in every @GetMapping method, If I need to log the username, I can add an annotation before the method:

@PostMapping
@LogUsername
public Course createCourse(@RequestBody Course course){
return courseService.saveCourse(course);
}

Finally, this is the result:

2018-10-21 08:29:07.206  INFO 8708 --- [nio-8080-exec-2] com.khoa.aop.LogUsername                 : john.doe

How to get the current logged in user in Spring Boot with Keycloak?

Using class KeycloakPrincipal only, gives the warning

Raw use of parameterized class 'KeycloakPrincipal'

but it works!

[...]
if (authentication.getPrincipal() instanceof KeycloakPrincipal) {
KeycloakPrincipal principal = (KeycloakPrincipal) authentication.getPrincipal();
return principal.getName();
[...]

Spring Boot - make sure data belongs to current logged in user

Checking if the user has permissions to a project on every request is the correct solution. Consider cases when many other applications / users are calling your API. You want to make sure that your API is as secure as possible and cannot be manipulated from the frontend.

To make it more efficient you should have a way/query to check associations in your database like a simple query that returns true/false which should be quicker than retrieving all the data and comparing in Java code.

And when possible combine multiple database queries into one, like for one of your examples:

GET /api/v1/projects/{projectId}

in this case, don't run a query to get a user's information and a query for the requested project. Instead you could do a single query with a join between the user's table and the project table which should only return a project if the user is associated with it. The best way really depends on how your database is structured.

Adding a user id into the API URL is just redundant information. Just because the user id in the token matches the user id in the URL doesn't mean the user has any kind of permissions to any project.

Another solution to be avoided is to include the user's project ids in the JWT token which you can then compare without making a database request. This is bad for several reasons:

  1. The token should only have required information for the user to access the API, it shouldn't have business logic
  2. Depending on how much business logic you store in the token the token can become large in size. See this post for a discussion on size limits: What is the maximum size of JWT token?
  3. If there is a way for the someone other than the user (like admin) to add/remove a user's association to a project then that change will not be reflected in the token until the token's data is refreshed

EDIT:

On the spring side I have used the @PreAuthorize annotation before to handle these types of method checks. Below is pseudo code as an example.

@RestController
public class MyController {

@PostMapping
@PreAuthorize("@mySecurityService.isAllowed(principal, #in)")
public SomeResponseType api1(SomeRequestType requestData) {
/* this is not reached unless mySecurityService.isAllowed
returns true, instead a user gets a 401/403 HTTP response
code (i don't remember the exact one) */
}
}

@Service
public class MySecurityService {

/*
Object principal - this is spring's UserDetails object that is
returned from the AuthenticationProvider. So basically a Java
representation of the JWT token which should have the
user's username.

SomeRequestType requestData - this is the request data that was
sent to the API. I'm sure there is a way to get the project ID
from the URL here as well.
*/
public boolean isAllowed(Object principal, SomeRequestType requestData) {
/*
take the user's username from the principal, take the
project ID from the request data and query the database
to check authorization, return true if authorized

make this check efficient
*/

return false;
}
}

The annotation and the security service can then be applied to multiple methods. You can have many different security services depending on what your are checking.

There are other ways available too https://www.baeldung.com/spring-security-method-security and this has to be enabled in spring's configuration (also explained in the link).

Getting the current logged in user name when using Spring Security

What you need is called @AuthenticationPrincipal.
You can inject it in controller method like this:

@GetMapping("/")
public void get(@AuthinticationPrincipal User user){ ... }

Here is documentation

Alternatively, you can create your own annotation and custom argument resolver, and inject whatever you want.

How can I have list of all users logged in (via spring security) my web application

For accessing the list of all logged in users you need to inject SessionRegistry instance to your bean.

@Autowired
@Qualifier("sessionRegistry")
private SessionRegistry sessionRegistry;

And then using injcted SessionRegistry you can access the list of all principals:

List<Object> principals = sessionRegistry.getAllPrincipals();

List<String> usersNamesList = new ArrayList<String>();

for (Object principal: principals) {
if (principal instanceof User) {
usersNamesList.add(((User) principal).getUsername());
}
}

But before injecting session registry you need to define session management part in your spring-security.xml (look at Session Management section in Spring Security reference documentation) and in concurrency-control section you should set alias for session registry object (session-registry-alias) by which you will inject it.

    <security:http access-denied-page="/error403.jsp" use-expressions="true" auto-config="false">
<security:session-management session-fixation-protection="migrateSession" session-authentication-error-url="/login.jsp?authFailed=true">
<security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" expired-url="/login.html" session-registry-alias="sessionRegistry"/>
</security:session-management>

...
</security:http>


Related Topics



Leave a reply



Submit