Consider an Struts 2 + Spring 4 project.
For each login the User object is put in session. As a very simple action it will look
public class LoginProcess implements ServletRequestAware {
#Inject
private AuthenticationServices authenticationServices;
public String execute() {
//The login method makes a new User and fills its setters
User newUser = authenticationServices.login(....);
getServletRequest().getSession().setAttribute("USER_SESSION", user);
}
}
As we manually make a new User object so it is not managed spring bean, and we can't use spring features in User class: #Inject , #Value ,...
I tried to change user as:
#Named
#Scope(value="session")
public class User { ...
#Inject
private AccountServices accountServices;
}
and inject the User in instead of calling new User, but I get the error:
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.request.SessionScope.get(SessionScope.java:91)
Well although it describes the error, but I can not find how can I fix it, and I am not sure if this is the correct way at all. It seems that I can only use spring session scope been when I am using spring mvc
Any comments ?!
Why I need this ?! (Simplified situation)
The user object has a getAccounts() methods which get all user accounts. Getting user accounts is an expensive operation, and it is possible that a user does not require its accounts during its login.
So, instead of get user accounts as soon as user logs in, we let the get method get user accounts if it does not have it:
public class User() {
private Accounts accounts;
#Inject
private AccountServices accountServices;
Accounts getAccounts() {
if (accounts == null) {
accounts = accountServices.getUserAccountsFromDB(...)
}
return accounts;
}
Don't create a new instance of User by yourself, instead get a bean from Spring context.
For example you can achieve it by implementing ApplicationContextAware interface and calling one of getBean methods.
User user = applicationContext.getBean(User.class);
// populate user and put it into session
In that way it is a Spring managed bean an all required properties should be injected.
BUT consider changing your User to a simple POJO and moving all business logic (such as fetching users accounts) to some more appropriate place, in that way your model layer will be cleaner and easily testable.
Related
I have an application. Each user corresponds to the entity "Client". During the session, the user works with his entity.
I have JAX-RS + EJB application. I want this entity to be visible and used in all services, as a singleton. I want to achieve it using CDI.
First, the user login to the application. After login, I want to get its entity from the base and assign it to a singleton, but I didn't succeed. I tried the #Singleton annotations (javax.inject), #SessionScoped and #ApplicationScoped.
How can I do it?
//Entity
#Entity
#SessionScope //or #Singlton/#ApplicationScope
class Client { fields }
//login service
#Inject
Client client;
//After login I want to assign an entity from db[cleintEntity]
//to entity which is available everywhere[client]
client = clientEntity;
I want to do this:
//some ejb
#Inject
Client client;
//use entity
I don't want to transmit a link to the instance throughout the application, but I want it to be available everywhere using CDI.
Or do I need to create a separate ClientDAO, which will be my singleton?
Based on your comment, you must use the #SessionScope. The #Singleton and #ApplicationScope could be used if you wanted to have a single instance for all your users.
To solve your problem:
You want to instantiate the Client when user logs in. So when you're in the login service, this object is not instantiated yet, so you can't #Inject it into your login service and you should remember to remove the #Inject annotation.
You need to use the #Produces annotation. This annotation is used when you want to have control on the way your class is getting instantiated. When CDI container wants to find an implementation of the Client class, if it finds a method that returns Client and has #Produces annotation, it calls that method instead of just instantiating the Client itself. You should do your login business, then create an instance of Client and store it in your login service as a member variable. Then add a method in this class which returns that client object and annotate the method with #Produces. The final structure of your login service would be something like this:
#SessionScope
public class LoginService {
private Client client;
public void login(String username, String password) {
// implement login business
// if login is successful
client = clientEntity;
}
#Produces
#SessionScope
public Client clientProducer() {
return this.client;
}
}
You can also put the #Produces annotation on top of a field. In such cases, the CDI container will use the value stored on that field instead of calling a method.
#SessionScope
public class LoginService {
#Produces
#SessionScope
private Client client;
public void login(String username, String password) {
// implement login business
// if login is successful
client = clientEntity;
}
}
Of course this should be considered as a sort of pseudocode. I don't know about all the details of your business. Maybe the way you're implementing the logic is completely wrong ;). But to solve this specific problem, the #Produces should work.
Working with Spring / Spring security on a small project at the moment and having difficulty implementing this feature. Ideally I only want user1 to view the details of user1 and not those of 2,3 or 4 for example.
I've implemented Spring Security with Roles and understand that I can retrieve a UserDetails object or a principle, I'm not sure exactly but I know I can retrieve the details of the current logged in user using one of, what appears to be many methods.
This is what I'm currently using as a proof of concept when we go to the Admin/home page:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Gamer gamer = gamerService.findGamerByEmail(auth.getName());
System.out.println("Auth: " + auth.getName());
System.out.println("Gamer: " + gamer.getName() + gamer.getId() + gamer.getEmail());
The security config takes care of whether or not the current user can access because of the roles assigned to it.
I believe I should be able to go to the url of /mysite/viewUserDetails and have that page display information of the current user but I cannot find any examples of this, I've found plenty of example that prove a logged in user can view a page but none that specify checks in place to ensure user1 can only view user1's details.
On an older page I do this to display information for a particular user but I understand this to be bad practice-
<a th:href="#{/gamer/{gamerid}/games/excited (gamerid=${gamer.id}) }">
*Worth noting that this isn't using any form of login/registration to pull out this info, I'm simple using the id I pass in as part of the DB query.
It maps onto :
#RequestMapping("/gamer/{gamerid}/games/excited")
public String getExcited(#PathVariable final Long gamerid, Model model){
addGamerListAttributes(model, gamerid, "EXC");
return "games";
}
So my question becomes, and I hope you can point in the right direction, How can I implement a solution where a user can only view his/her details and how should this be represented via the form and connecting controllers as passing ids in the url is kinda ugly (I could use a guid but...)
Thanks so much in advance.
It's actually quite an easy choice. Either you have an entry point like:
#RequestMapping("/gamer/{gamerid}/games/excited")
and you manually check that the user in session can access the requested resource, or you have something like
#RequestMapping("/my-games")
that automatically reads the user id from the security context.
More than a security choice, I'd pick one depending on code reuse and future use-cases (for example the same page/set of pages can be seen by more than one user).
Have a look at #PreAuthorize annotation. It is possbile to annotate given endpoint with it and create custom logic in a bean. Then you can use custom method to allow or disallow the endpoint to proceed :
#Controller
public class HomeController {
#Autowired
private AuthenticationService authenticationService;
#RequestMapping("/gamer/{gamerid}/games/excited")
#PreAuthorize("#authenticationService.hasAccess(#gamerid)")
public String getExcited(#PathVariable final Long gamerid, Model model){
addGamerListAttributes(model, gamerid, "EXC");
return "games";
}
}
Service class :
#Service
public class AuthenticationService {
public boolean hasAccess(String tgamerid) {
//implement logic here
return true;
}
}
Method hasAccess in the AuthenticationService should return boolean. #PreAuthorize will be launched before controller handler method is invoked. The controller above is just an example. You can pass Authentication object in SPeL expression in #PreAuthorize annotation to service method or get it from security context inside service class, to implement logic which fits your needs. More information can be found here and in Spring Docs.
I'd like to know if it is safe to inject the persistent entity via #AuthenticationPrincipal in a method annotated with #RequestMapping.
My concern is following:
#GetMapping("/orders")
public List<OrderDto> getOrders(#AuthenticationPrincipal User user) { ... }
in the code above, the method should return all orders for currently logged user (which it does), however, if I modify the code as follows
#GetMapping("/orders")
public List<OrderDto> getOrders(User user) { ... }
then I'm able to retrieve orders for all users (by changing the user's role to admin in the http request). Is something like this possible while using #AuthenticationPrincipal? How does the #AuthenticationPrincipal annotation ensure that the possible attacker would not be able to inject some malicious code to the entity?
To clarify, the User entity is annotated with #Entity.
I'm currently using Spring Security 5.1.4.
I've created a OAuth2 Resource Server that accepts and validates JWT tokens and extracts user information from the claims in order to determine user information such as username and authorities. I've largely done this using spring-security-oauth2-autoconfigure library.
After a user is authenticated, I'd like to call custom code that puts a message on a Kafka stream to indicate that a user has logged in. Where is the most appropriate place to do this?
I could do this in OAuth2AuthenticationManager.authenticate, but I'd have to extend that class and override that method, and then wire it in. It seems like Spring should have something already in place to handle this.
OAuth2AuthenticationProcessingFilter, which calls the authenticate method mentioned in the question, has a member called eventPublisher. Its methods include publishAuthenticationSuccess, which is called following a successful authentication.
To tie custom code in to this, create an event listener that gets picked up as a bean by Spring. Something like this:
#Component
public class MyAuthenticationEventListener implements ApplicationListener<AuthenticationSuccessEvent> {
private static final Logger logger = LoggerFactory.getLogger(MyAuthenticationEventListener.class);
#Override
public void onApplicationEvent(AuthenticationSuccessEvent authenticationSuccessEvent) {
logger.info("User logged in: " + authenticationSuccessEvent.getAuthentication().getName());
}
}
I am creating a stateless REST API with spring boot.
Therefore I am using a token based authentication.
Currently the logout functionality is only implemented on the client side.
I just clear all cookies.
Problem is that the user object seems to survive the request so it still exists in the next requests.
My service to get the current user is simply:
#Service
public class UserService {
private User user;
#Autowired
private UserRepository;
public User get() {
if (user != null) {
return user;
}
Integer id = (Integer) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
user = userRepository.findById(id);
return user;
}
}
I would expect that the user variable is null on every request? The funny thing is that the correct user id is set in the security context. But the service returns the user object because it already exists.
You shouldn't use user as a class attribute.
UserService is a singleton, what happens when you have concurrent requests coming from different users?
Move this variable inside the get method.
Moreover, if you are using JWT as the token based authentication take a look at this project.
With JWT you can retrieve the user required informations directly from the token without performing any queries.