Custom Implicit Type Variable in Spring - java

I want to implement a public/global variable so that I can access from any layer(controller/service/dao) of a spring project. For example
class Abc{
public User user;
public String subdomain;
}
Now I want to get user, subdomain values from any layer. But remember that, my project has user management. So I need to specific value for each user session.
Note:
The life cycle of this values is session
This is not singletone forall users
This is specific per session
Thanks

A possible solution is to have a service which has the ability to lookup the currently logged on user, and provides the context information you require. As #siledh mentioned, once you have this information, you can then pass it into layers where you do not want to have any concept of context (e.g. in your DOAs)
#Service
public class ContextService {
public User getLoggedOnUser() {
// Get user id/username from Spring Security principal
// Use id/username to lookup the User
// Return the logged on user
}
}
#Controller
public class SomeController {
#Autowired
private ContextService context;
#Autowired
private SomeReposity someReposity;
#RequestMapping("/home")
public String homePage() {
User loggedOn = contextService.getLoggedOnUser();
String userInformationNeededForHomePage =
someReposity.findSomethingForUser(loggedOn);
...
}
}
#Repository
public class SomeReposity {
public String findSomethingForUser(User user) {
// find something
}
}

Related

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!

When and how to instantiate a Spring Bean in my Rest Api

First of all, I'm a relative noob to Spring Boot, so keep that in mind.
I've got a REST api in which I'm trying to minimize database calls for the same object and I've determined that using a Spring Bean scoped to the Request is what I want. Assuming that is correct, here is what I'm trying to do:
1) Controller takes in a validated PhotoImportCommandDto command
PhotoCommandController
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> importPhoto(#Valid #RequestBody PhotoImportCommandDto command){
...
}
2) PhotoImportCommandDto is validated. Note the custom #UserExistsConstraint which validates that the user exists in the database by calling a service method.
PhotoImportCommandDto
#Component
public class PhotoImportCommandDto extends BaseCommand {
#NotNull(message = "userId must not be null!")
#UserExistsConstraint
private Long userId;
...
}
What I would like to do is somehow set a Spring Bean of the user that is validated in the #UserExistsConstraint and reference it in various methods that might be called throughout a Http request, but I'm not really sure how to do that. Since I've never really created my own Spring Beans, I don't know how to proceed. I've read various guides like this, but am still lost in how to implement it in my code.
Any help/examples would be much appreciated.
You can use the #Bean annotation.
#Configuration
public class MyConfiguration {
#Bean({"validUser"})
public User validUser() {
User user;
//instantiate user either from DB or anywhere else
return user;
}
then you can obtain the validUser.
#Component
public class PhotoImportCommandDto extends BaseCommand {
#Autowired
#Qualifier("validUser")
private User validUser;
...
}
I don't really know how to make annotations in Java. Anyway, in Spring, checking where the User exists in the DataBase or not is one line of code:
userRepository.findOne(user) == null
That is accomplished by the Spring Data JPA project:
Create a JPA Entity User.
Set the spring.datasource.url and login/password in the
resources/application.properties.
Create this interface:
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
}
Note, Spring implements it behind the scences.
Inject this interface into your RestController (or any other Spring bean):
private UserRepository userRepository ;
**constructor**(UserRepository ur){
userRepository = ur;
}
Note, a Spring Bean is any class annotated #Component (this includes stereotype annotations like Controller, Repository - just look up the contents of an annotation, it may use #Component internally) or returned from a method which is annotated #Bean (can only be on the Component or Configuration class). A Component is injected by searching the classpath, Bean is injected more naturally.
Also note, injecting is specifying #Autowired annotation on field or constructor, on a factory method, or on a setter. The documentation recommends that you inject required dependencies into constructor and non-required into the setter.
Also note, if you're injecting into a constructor and it is clean by the arguments, you may omit #Autowired annotation, Spring will figure it out.
Call its method findOne.
So, you can do one of the following:
Inject the userRepository into the #RestController constructor (as shown above). I would do that.
Inject the userRepository into the #Service (internally #Component) class that will do this sorts of thing for you. Maybe you can play with it to create an annotation.
p.s. Use #PostMapping instead of #RequestMapping(method = RequestMethod.POST)
p.p.s. If ever in doubt, go to the official documentation page and just press CTRL-F: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/ Note the current word, that will always take you to the latest version.
p.p.p.s Each Spring project has its own .io webpage as well as quick Get Started Guides where you can quickly see the sample project with explanations expecting you to know nothing.
Hope that helps! :)
Don't forget to mark the answer as accepted if you wish
Using Jose's input, I took a bit of a different route.
Here's what I did:
I created a ValidatedUser class:
#RequestScope
#Component
public class ValidatedUser {
private UserEntity user;
public UserEntity getUser() {
return user;
}
public void setUser(UserEntity user) {
this.user = user;
}
}
and I also created a wrapper class HttpRequestScopeConfig to capture all variables to use over the course of an Http Request to the api.
#Component
public class HttpRequestScopeConfig {
#Autowired
private ValidatedUser validatedUser;
...
public UserEntity getValidatedUser() {
return validatedUser.getUser();
}
public void setValidatedUser(UserEntity validatedUser) {
this.validatedUser.setUser(validatedUser);
}
...
}
In my UserExistsConstraintValidator (which is the impl of #UserExistsConstraint, I set the validatedUser in the httpRequestScopeConfig:
public class UserExistsConstraintValidator implements ConstraintValidator<UserExistsConstraint, Long> {
//private Log log = LogFactory.getLog(EmailExistsConstraintValidator.class);
#Autowired
private UserCommandService svc;
#Autowired
private HttpRequestScopeConfig httpRequestScope;
#Override
public void initialize(UserExistsConstraint userId) {
}
#Override
public boolean isValid(Long userIdField, ConstraintValidatorContext context) {
try {
UserEntity user = svc.findUserOfAnyStatus((Long) userIdField);
if (user != null) {
httpRequestScope.setValidatedUser(user);
return true;
}
} catch (Exception e) {
//log.error(e);
}
return false;
}
}
Now, I can access these variables throughout the rest of my service layers by autowiring HttpRequestScopeConfig where necessary.

How to read session value in Service layer when the value was saved in Play controller

I am using Play 2.4. I have a controller which extends play.mvc.Controller. There I am setting user object in session as below:
session("username", user);
Now I want to read this session object in my service class which doesn't extend play.mvc.Controller, am not getting how I can read this, so please assist.
It is possible by using the Http.Context class:
import play.mvc.Http;
class Service {
public void something() {
String username = Http.Context.current().session().get("username");
// do something
}
}
But, should you? It is very unusual that a layer that is below (service) know things about the layer above (controllers). The code above is harder to test, per instance. Why not rewrite it so that your service receives what it needs from the above layer?
class Service {
public void something(String username) {
// do something
}
}
And your controller will do:
class MyController extends Controller {
public Result action() {
String username = session("username");
service.something(username);
}
}

Chain DAO methods in Spring/Hibernate for Lazy initializations

I have some model classes with relations to each other (User, Group, Message, etc). For many reasons (I'm not giving the details, but this is not a flexible decision) the relations are Lazy, and I want them to remain Lazy.
Sometimes I want to load some class collections. P.e. user.getGroups() or user.getMessages() but, because of the Lazy load, I need to call Hibernate.initialize() in the method of the DAO class, which is OK for me.
The question is, Is there a strategy to avoid declaring many DAO methods to load several combination of collections, reusing the methods?
Here is an example of what I want to avoid:
UserController:
public class UserController {
#Autowired
private UserService userService;
#RequestMapping(value = "/user/view/{id}", method = RequestMethod.GET)
public String view(#PathVariable Long id, Model model) {
// Choose one between the following, depending on the case
User user = userService.getUser(id);
User user = userService.getUserWithGroups(id);
User user = userService.getUserWithGroupsAndMessages(id);
//...
}
}
And the UserDAOImpl:
#Repository
public class UserDAOImpl implements UserDAO {
#Override
public User getUser(long id) {
return (User) this.getCurrentSession().get(User.class, id);
}
#Override
public User getUserWithGroups(long id) {
User user = (User) this.getCurrentSession().get(User.class, id);
Hibernate.initialize(user.getGroups());
return user;
}
#Override
public User getUserWithGroupsAndMessages(long id) {
User user = (User) this.getCurrentSession().get(User.class, id);
Hibernate.initialize(user.getGroups());
Hibernate.initialize(user.getMessages());
return user;
}
}
The point is I want to avoid creating multiple DAO methods for each combination of collections that must be loaded for each case. I'd like to achieve a call syntaxis in the Controller similar to User user = userService.getUser(id).initGroups().initMessages();, to chain only the specific methods I need in every case.
But in this particular example, the initXXX() methods would be in the model class User, which may not contain any #Autowired service, and because of that is not a possible solution.
Any ideas?
--EDIT--
Some non-working ideas:
Option 1. Declare initGroups() in User model class:
User
#Entity
#Table(name="user")
public class User implements Serializable {
//...
public User initGroups() {
Hibernate.initialize(getGroups());
return this;
}
}
Called from Controller this way: User user = userService.getUser(id).initGroups();
Option 2. Declare initGroups() in UserService and UserDAO:
UserServiceImpl
#Service
#Transactional
public class UserServiceImpl implements UserService {
//...
#Override
public User initGrupos(User user) {
return userDAO.initGroups(user);
}
}
UserDAOImpl
#Repository
public class UserDAOImpl implements UserDAO {
//...
#Override
public User initGroups(User user) {
Hibernate.initialize(user.getGroups());
return user;
}
}
Called from Controller this way:
User user = userService.getUser(id);
user = userService.initGroups(user);
Result (both the same):
org.hibernate.HibernateException: collection is not associated with any session
org.hibernate.collection.internal.AbstractPersistentCollection.forceInitialization(AbstractPersistentCollection.java:484)
org.hibernate.Hibernate.initialize(Hibernate.java:78)
...
The init methods don't need the service, they just need to call Hibernate.initialize while maintaining the previous Hibernate session which loaded the object in the first place (that is the most important part).

Best practices for user management with Play! Framework?

What's the recommended way to handle user management in Play! Framework?
This is my setup:
UserAwareControllerBase as a base class for controllers
The main view template includes a login/logout button
custom Security class extending Secure.Security, and controllers that only allow signed-in users are annotated with #With(Secure.class)
(I haven't implemented a real password/login system yet, just entering the correct email suffices to login. TBD)
The setup is nice because Controllers don't need to bother writing user management code, and can easily get the signed-in user by calling getUser(). However, I'm already starting to feel the limitations of this setup. I'm getting a convoluted inheritance hierarchy, and am facing a problem if I want to inherit from the CRUD class.
What's the best practice for handling user authentication/authorization in Play! without repeating code?
UserAwareControllerBase.java
public abstract class UserAwareControllerBase extends Controller {
protected final static UserRepository userRepo = new UserRepository();
#Before
static void setConnectedUser() {
if(Security.isConnected()) {
User user = userRepo.findByEmail(Security.connected());
renderArgs.put("user", user);
}
}
static User getUser() {
return renderArgs.get("user", User.class);
}
}
template.html
<div id='header'>
...
#{if user}
Log out (${user.email})
#{/if}
#{else}
Log in
#{/else}
</div>
Security.java
public class Security extends Secure.Security {
protected final static UserRepository userRepo = new UserRepository();
static boolean authenticate(String username, String password) {
User user = userRepo.findByEmail(username);
return user != null;
}
public static void onDisconnected() {
Application.index();
}
}
If you want to share code between controllers, prefer using the #With annotation rather than using inheritance.
For user management, I am used to put some rights in the session in the onAuthenticated method like this
static void onAuthenticated() {
session.put(Const.MY_RIGHT, true);
}
And then my check method is
static boolean check(String profile) {
return Boolean.parseBoolean(session.get(profile));
}
With this I can use the #check annotation to check user rights. In the onAuthenticated method you can do whatever you want to map complex rights managements into simple constants.

Categories

Resources