Spring: #RequestScope - annotated method does not get called - java

In the application I would like to create a request-scoped bean (annotated with #RequestScope) that would represent a user of the application (for authentication OAuth2 is used with the company's own authentication provider based off keycloak; the problem is that the Principal doesn't contain extra information about the permissions this particular user has and they have to be retrieved from another service, which is why I want to have this request-scoped bean that would retrieve fresh permissions on each request).
public class MyApplicationUser {
private final String name;
private final List<Permission> permissions;
/* all-arg constructor, getters */
}
#Configuration
public class UserConfiguration {
private final PermissionService permissionService;
/* constructor, etc */
#Bean
#RequestScope
public MyApplicationUser currentUser(#CurrentSecurityContext(expression = "authentication") OAuth2AuthenticationToken authenticationToken) {
/* call permissionService, map the result and the token details data to MyApplicationUser instance and return it*/
}
}
Although on each request a new instance of MyApplicationUser is created, it is not created by calling the currentUser method (it never gets called), but rather it seems that spring uses the provided constructor and supplies nulls as parameters, which is not what I want. How do I fix that?
P.S. the main class extends SpringBootServletInitializer

Related

How to access Principal in controller methods without it declared as argument?

I added Spring Security on a Rest API with JWT authentication. Now I need to retrieve some data from the token in every controller method - be it either the username or other information.
Since almost all of my controller methods would need a Principal variable, is there a way to avoid declaring it as an argument to each method?
I once used ObjectProvider to do a similar thing, like:
#RequestScope
#Component
public class MyObj // ...
Usage:
#Component
public class OtherObj {
#Autowired
private ObjectProvider<MyObj> provider;
// ...
#Override
public boolean meth() throws Exception {
MyObj o = provider.getIfAvailable();
// ...
But there I found that if no instance exists, it is created instead of being returned null or an exception being thrown.
You can create one utility class, which provides you the principal.
public static Principal getPrincipal() {
SecurityContext securityContext = SecurityContextHolder.getContext();
return securityContext.getAuthentication().getPrincipal();
}
Ofcourse, here you would need to put the null checks in case the context or authentication is null.

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

I would like to get the username of the user in every request to add them to log file.
This is my solution:
First, I created a LoggedUser with a static property:
public class LoggedUser {
private static final ThreadLocal<String> userHolder =
new ThreadLocal<>();
public static void logIn(String user) {
userHolder.set(user);
}
public static void logOut() {
userHolder.remove();
}
public static String get() {
return userHolder.get();
}
}
Then I created a support class to get username:
public interface AuthenticationFacade {
Authentication getAuthentication();
}
#Component
public class AuthenticationFacadeImpl implements AuthenticationFacade {
#Override
public Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
}
Finally, I used them in my Controllers:
#RestController
public class ResourceController {
Logger logger = LoggerFactory.getLogger(ResourceController.class);
#Autowired
private GenericService userService;
#Autowired
private AuthenticationFacade authenticationFacade;
#RequestMapping(value ="/cities")
public List<RandomCity> getCitiesAndLogWhoIsRequesting(){
loggedUser.logIn(authenticationFacade.getAuthentication().getName());
logger.info(LoggedUser.get()); //Log username
return userService.findAllRandomCities();
}
}
The problem is I don't want to have AuthenticationFacade in every #Controller, If I have 10000 controllers, for example, it will be a lot of works.
Do you have any better solution for it?
The solution is called Fish Tagging. Every decent logging framework has this functionality. Some frameworks call it MDC(Mapped Diagnostic Context). You can read about it here and here.
The basic idea is to use ThreadLocal or InheritableThreadLocal to hold a few key-value pairs in a thread to track a request. Using logging configuration, you can configure how to print it in the log entries.
Basically, you can write a filter, where you would retrieve the username from the security context and put it into the MDC and just forget about it. In your controller you log only the business logic related stuff. The username will be printed in the log entries along with timestamp, log level etc. (as per your log configuration).
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
Well, you are already accesing authentication object directly from SecurityContextHolder, you can do it in your controller.
#RequestMapping(value ="/cities")
public List<RandomCity> getCitiesAndLogWhoIsRequesting(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication != null){
//log user name
logger.info(authentication.get());
}
return userService.findAllRandomCities();
}
If you do not want to put all this in every endpoint, an utility method can be created to extract authentication and return its name if found.
public class UserUtil {
public static String userName(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication == null ? null : authentication.getName();
}
}
and call it in your endpoint like
#RequestMapping(value ="/cities")
public List<RandomCity> getCitiesAndLogWhoIsRequesting(){
//log user name
logger.info(UserUtil.username());
return userService.findAllRandomCities();
}
However, you are still adding lines of code in every endpoint, and after a few of them it starts to feel wrong being forced to do it. Something I suggest you to do is try aspect oriented programming for this kind of stuff. It will require you to invest some time in learning how it works, create annotations or executions required. But you should have it in a day or two.
With aspect oriented your endpoint could end like this
#RequestMapping(value ="/cities")
#LogUserName
public List<RandomCity> getCitiesAndLogWhoIsRequesting(){
//LogUserName annotation will inform this request should log user name if found
return userService.findAllRandomCities();
}
of course, you are able to remove #LogUserName custom annotation and configure the new aspect with being triggered by methods inside a package, or classes extending #Controller, etc.
Definitely it is worth the time, because you can use aspect for more than just logging user name.
You can obtain the username via request or parameter in your controller method. If you add Principal principal as a parameter, Spring Ioc Container will inject the information regarding the user or it will be null for anonymous users.
#RequestMapping(value ="/cities")
public List<RandomCity> getCitiesAndLogWhoIsRequesting(Principal principal){
if(principal == null){
// anonymous user
}
}
There are various ways in Spring Security to fetch the user details from the security context. But according to your requirement, you are only interested in username, so you can try this:
#RequestMapping(value ="/cities")
public List<RandomCity> getCitiesAndLogWhoIsRequesting(Authentication authentication){
logger.info(authentication.getName()); //Log username
return userService.findAllRandomCities();
}
Hope this helps!

How to initialize a object in a interface?

I have this MovieService.java:
#Service
public class MovieService implements MovieInterface {
#Autowired
private MovieRepository movieRepository;
#Autowired
private UserRepository userRepository;
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
JwtUser user = (JwtUser)authentication.getPrincipal();
User current_user = userRepository.findOne(user.getId());
#Override
public Movie createMovie(Movie movie) {
current_user.addMovie(movie);
userRepository.save(current_user);
return movie;
}
}
This is the error I'm getting when I'm compiling my code:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'movieController': Unsatisfied dependency expressed through field 'movieService'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'movieService' defined in file [C:\Users\alucardu\Documents\projects\movieseat\backend\target\classes\com\movieseat\services\MovieService.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.movieseat.services.MovieService]: Constructor threw exception; nested exception is java.lang.NullPointerException
This problem is resolved when I move the authentication, user and current_user properties into the createMovie method. But I want to use current_user in multiple methods so I would like to add it as a class member.
I also implement a MovieInterface:
package com.movieseat.interfaces;
// Java imports
import java.util.List;
import com.movieseat.model.security.User;
// Project imports
import com.movieseat.models.Movie;
import com.movieseat.security.JwtUser;
import org.springframework.security.core.Authentication;
public interface MovieInterface {
List<Movie> getAllmovies();
Movie createMovie(Movie movie);
void deleteMovie(Integer id);
}
So I thought maybe the code doesn't compile because the properties authentication, user and current_useraren't defined in the interface. Although I would expect a different error output for something like that. When I add the properties to the interface:
public interface MovieInterface {
Authentication authentication;
JwtUser user;
User current_user;
List<Movie> getAllmovies();
Movie createMovie(Movie movie);
void deleteMovie(Integer id);
}
I get the message:
the blank final field authentication may not have been initialized
the blank final field user may not have been initialized
the blank final field current_user may not have been initialized
This message makes sense since the interface expects the fields to be final. And these fields have no value. So my question is, can I mark a field not final in a interface? And if I have to initialize a value for the fields what do I need to use?
I know you initialize a field String name = "name"; But these properties are objects.
User current_user = {};
Cannot convert from Object[] to User
Your service is a bean, and especially it is a singleton. That means there is only one instance of this service in your application.
But it may be called by different users. So you have to get the current user in your createMovie method (you already named it current User!).
When you try to initialize these fields in a PostConstruct method, you won't have a SecurityContext and you won't have a principal. And if you keep the info about the current user in the service instance, then all calls would be made on behalf of this one user, no matter where they come from.
So the best thing is to create a method :
private getUser() {
Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();
JwtUser user = (JwtUser)authentication.getPrincipal();
User current_user = userRepository.findOne(user.getId());
return current_user;
}
and call that from within your different methods.
Your class is a bean. This means, all #Autowired fields will be set from someone else (Spring or CDI). Question is WHEN?. You might have bean A with auto wired bean B as property and the other way around. You need to wait, until all properties have been auto wired. You try to use userRepository during object creation. That's too early. Spring has to create the Java object (no way around this), but will do the autowiring after that. But with dependency injection, you will get told when all properties have been set, if you annotate a method with #PostConstruct.
#PostConstruct
public void init(){
// it is safe now to use user repository, because it is not null anymore
}

Reuse object and its methods in code of multiple #ManagedBeans

I want to set my User object only once through my #RequestScoped LoginBean. Then I want to reuse its setters, getters and an isLoggedIn() method in other #ManagedBean through CDI.
Request Scoped Class that Sets User Object
#ManagedBean
#RequestScoped
public class LoginBean {
#ManagedProperty(value = "#{bean}")
protected Bean bean;
private String username;
private String password;
public String login() {
bean.setLoggedInUser(userDao.getUser(username));
return "index";
}
// Getters and Setters, including for the #ManagedProperty baseBean.
}
SessionScoped Class that Stores User Object
#ManagedBean
#SessionScoped
public class Bean {
private User loggedInUser = null;
public boolean isLoggedIn() {
return loggedInUser != null;
}
// Getters and setters for loggedInUser
}
Class Where I Want To Refer to loggedInuser
#ManagedBean
#RequestScoped
public class ShowUserDetails extends Bean {
private Details details = new Details();
public void showDetails() {
if(isLoggedIn()) { // THIS ALWAYS RETURNS FALSE
// Do stuff
}
}
}
Solutions So Far
I can list a Bean #ManagedProperty in every single Backing Bean that needs the loggedInUser. This seems wrong as I am copy-pasting two lines in every class.
I can get the instance of Bean class from FacesContext using context.getApplication().evaluateExpressionGet(). This allows me to have one method to retrieve Bean instance in a superclass, however this also seems wrong. That said, this is the method I will go with if I am unable to find a pure CDI solution.
You are worried about adding two lines of code once in each bean, yet you do want to write
if(isLoggedIn()) { // THIS ALWAYS RETURNS FALSE
// Do stuff
}
And most likely many times in a bean (and most likely also things in an else statement).
Then I'd certainly go fo using annotations and interceptors
See also
https://code.google.com/p/jee6-cdi/source/browse/tutorial/cdi-aop-example/src/main/java/org/cdi/advocacy/security/
http://balusc.blogspot.nl/2013/01/apache-shiro-is-it-ready-for-java-ee-6.html
My solution was to use the HttpSession to store the User object instead of having a class member variable. This allowed me to have one class that handled the getting/setting and all the other classes could simply call 'getLoggedinUser' to retrieve the entire object without hitting the DB.
private HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true);
private static final String LOGGED_IN_USER = "loggedInUser";
public User getLoggedInUser() {
return (User) session.getAttribute(LOGGED_IN_USER);
}
public void setLoggedInUser(User user) {
session.setAttribute(LOGGED_IN_USER, user);
}`

Injecting principal into resource method in RESTEasy with Guice

I am developing a REST API using RESTEasy with Guice and at the moment I am trying to incorporate basic authentication by use of an annotation similar to the #Auth found in Dropwizard. With
#Path("hello")
public class HelloResource {
#GET
#Produces("application/json")
public String hello(#Auth final Principal principal) {
return principal.getUsername();
}
}
the hello resource invocation should be intercepted by some code performing basic authentication using the credentials passed in the Authorization HTTP request header and on success injecting the principal into the method principal parameter. I would also like to be able to pass a list of allowed roles to the annotation, e.g. #Auth("admin").
I really need some advice in what direction to go to achieve this?
I think your best bet would be using an intermediate value within request scope. Assuming that you didn't put HelloResource in singleton scope, you can inject this intermediate value in some ContainerRequestFilter implementation and in your resource, and you can fill it inside this ContainerRequestFilter implementation with all authentication and authorization info you need.
It will look something like this:
// Authentication filter contains code which performs authentication
// and possibly authorization based on the request
#Provider
public class AuthFilter implements ContainerRequestFilter {
private final AuthInfo authInfo;
#Inject
AuthFilter(AuthInfo authInfo) {
this.authInfo = authInfo;
}
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// You can check request contents here and even abort the request completely
// Fill authInfo with the data you need
Principal principal = ...; // Ask some other service possibly
authInfo.setPrincipal(principal);
}
}
#Path("hello")
public class HelloResource {
private final AuthInfo authInfo;
#Inject
HelloResource(AuthInfo authInfo) {
this.authInfo = authInfo;
}
#GET
#Produces("application/json")
public String hello() {
// authInfo here will be pre-filled with the principal, assuming
// you didn't abort the request in the filter
return authInfo.getPrincipal().getUsername();
}
}
public class MainModule extends AbstractModule {
#Override
protected void configure() {
bind(AuthFilter.class);
bind(HelloResource.class);
bind(AuthInfo.class).in(RequestScoped.class);
}
}
And even if you did put the resource (or even the filter) in singleton scope for some reason, you can always inject Provider<AuthInfo> instead of AuthInfo.
Update
It seems that I was somewhat wrong in that the filter is by default not in singleton scope. In fact it seem to behave like singleton even though it is not bound as such. It is created upon JAX-RS container startup. Hence you will need to inject Provider<AuthInfo> into the filter. In fact, the container startup will fail if AuthInfo is injected into the filter directly while being bound to request scope. Resource (if not explicitly bound as singleton) will be OK with direct injection though.
I have uploaded working program to github.

Categories

Resources