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.
Related
I'm making a simple spring boot application and I want to incorporate filtering data by logged-in user or user session. But I don't know what is the best way to set up the application in a way that every user can access data specific to him. I have made the authentification and authorization part of the app.
I'm also working on that kind of application and I've done this by adding as argument#AuthenticationPrincipal CustomUserDetails userDetails to method where you want to get userId. Then you can call: Integer userId = userDetails.getUserId();
So for example something like this:
#GetMapping("/demo")
public String showTemplate(#AuthenticationPrincipal CustomUserDetails userDetails) {
Integer userId = userDetails.getUserId();
return "index";
}
And then you can do with this userId whatever you want. For example, create method in repository findByUserId
I need to implement authorization mechanism in Keycloak which looks into my customer's DB for user's permission based in roles.
I can't manage such roles and permissions in Keycloak's admin console but I will have to write some SPI to implement my authorization logic. Something like implementing a User Storage Provider to look in DB for user/password autentication.
But I can't find how to implement it. I think that, after obtaining a valid token for the user in authentication process, the application should send that token along with the permission needed to perform an action so Keycloak (my own SPI implementation) would validate the token and search the DB to grant permission.
Maybe the flow of authorization is different to what I think (what I wrote before).
Any clue would be appreciated.
How should the application send the token + permission to Keycloak?
How to implement a SPI which responds to such petition and validates the token?
To answer your first question: Keycloak will handle the authorization flow for you, you don't need to worry about that part. All you need to do is to provide the User Storage SPI by implementing the methods of the UserStorageProvider interface.
Keycloak documentation: https://www.keycloak.org/docs/latest/server_development/index.html#_user-storage-spi
To also add the roles/permissions you need to implement the UserModel interface which has methods for handling roles/permissions. keycloak documentation: https://www.keycloak.org/docs/latest/server_development/index.html#model-interfaces
To combine these two, you would fetch the user record from the Oracle DB using the UserStorageProvider and fill in the UserModel fields (email, name, roles, etc..).
Ex pseudo code:
NOTE: look at keycloak documentation for a more detailed walkthrough: https://www.keycloak.org/docs/latest/server_development/index.html#simple-read-only-lookup-example
public class OracleDbUserStorageProvider implements UserStorageProvider, UserLookupProvider {
private Map<String, UserModel> loadedUsers = new HashMap<>();
private OracleDbRepository oracleDbRepository;
#Override
public UserModel getUserByUsername(String username, RealmModel realm) {
UserModel user = loadedUsers.get(username);
if (user == null) {
OracleUser oracleUser = oracleDbRepository.getUser(username);
if (oracleUser != null) {
user = convertToUserModel(oracleUser);
loadedUsers.put(username, user);
}
}
return user;
}
private UserModel convertToUserModel(OracleUser oracleUser) {
// take oracleUser attributes and assign to a UserModel instance
return userModel; // user model converted
}
}
Hope this helps set you in the right direction.
I have a successfully working Spring 4 MVC project with lots of RESTful web-services on the back-end. We use Hibernate 5 and Spring Security 4 as well.
1) We authenticate users from a third-party source, OpenAM 10.x, and this creates a token in the browser.
2) From the front-end, we pass in the Ajax call a request header which contains that OpenAM token.
3) In the back-end, using the SiteMinder example, have a service: CustomUserDetailsService which does the following:
a) this uses our code to openAM and pass in the token
b) we get back JSON data we parse to get the username
c) from there we use the Hibernate Spring Security code to get further details for this user and get the roles
System.out.println("loadUserByUsername: username : " + username);
UserAccountEntity userAccountEntity = userAccountDao.getByUsername(username);
System.out.println("loadUserByUsername: userAccountEntity : " + userAccountEntity);
if (userAccountEntity == null)
{
System.out.println("CustomUserDetailsService: userAccountEntity not found");
throw new UsernameNotFoundException("Username not found");
}
System.out.println("CustomUserDetailsService: START: springframework.user");
// this "User" object is: org.springframework.security.core.userdetails.User
User user = new User(userAccountEntity.getUsername(), userAccountEntity.getPassword(), true, true, true, true,
getGrantedAuthorities(userAccountEntity));
From here, we have working security on the URL endpoints ... this works great!
Now, here is the problem ... my boss wants to use our custom proprietary ACL system to make secure query calls. Making a call to get records, we want to make sure we get only the records the user has access to. We have our own ACL tables that allow us security for a user or role to certain objects. Suffice it to say, this was written before Spring Security ACL ever existed. I'd prefer to use Spring Security 4 ACL, but that is out of the question.
Consider that we have 4 levels, Entity, Dao, Services, Web-Services. A Single controller looks like this:
#RequestMapping(value = "", method = RequestMethod.GET, headers = "Accept=application/json")
public #ResponseBody ArrayList<SponsorEntity> getSponsorList1() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
User user = null;
if (principal instanceof User)
{
user = ((User) principal);
}
ArrayList<SponsorEntity> sponsorEntityList = (ArrayList) service.getAllList();
return sponsorEntityList;
}
Like I said, this goes through the CustomUserDetailsService and sets the User as defined above. So, the first questions is, in the DAO layer, what is the code I would use to pull this org.springframework.security.core.userdetails.User?
If I can get this User object from the DAO layer, then I can "pipe" that username into our old existing legacy code.
The second question ... in a DAO Unit Test, how can I create/setup this Security? This way I can test to see if my back-end code is really getting the username from an authenticated user.
Please let me know if you need any more information. Thanks!
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.
I am developing my very first application with Spring (4.0.2), Spring #MVC (4.0.2) and Spring Security (3.2.5). I am now able to successfully log in by using Spring Security but now a "Good practices" question has come to my mind:
Currently I hace implemented my own version of UserDetails and UserDetailsService in order to save the details fetched from the DB.
Which is the best way (cleaner) to get the UserDetails?
So far I am using these two alternatives:
Using the next code line in the method
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Adding the parameter Authentication auth and the next line inside the method
User user = ((CurrentUserDetails) auth.getPrincipal()).getCurrentUser();
My impression is that I am dirtying the code. Don't you think so?
On every project I have worked on, we have always implemented SecurityUtils class with at least the following methods:
/**
* Get the active authentication object.
* #param strict Whether to throw an exception if no authentication object is found.
* #return Authentication object. Can be null only in non-strict mode.
*/
public static Authentication getAuthentication(boolean strict) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (strict && authentication == null) {
throw new AuthenticationCredentialsNotFoundException("Missing authentication object.");
}
return authentication;
}
/**
* Get active person principal.
* #return Active person principal user. Never null.
* #throws AccessDeniedException in case there is no active person principal.
*/
public static PersonPrincipal getActivePersonPrincipal() {
Object principal = getAuthentication(true).getPrincipal();
if (!(principal instanceof PersonPrincipal)) {
throw new AccessDeniedException("Invalid principal '" + principal + "'.");
}
return (PersonPrincipal) principal;
}
This class usually sits in {projectPackage}.core.security package. Whenever any code needs access to the current user, it calls this class.
Also we never let controller layer tell service layer anything about authentication. Service layer (or even Hibernate event listeners) are always asking SecurityUtils about the current authentication.
Spring Security is able to put the Authentification in the request where it can be retrieved with HttpServletRequest.getUserPrincipal(). And in Spring MVC, the controller can have access to the principal simply by passing it as a parameter.
So, IMHO one of the simpler way to access the login user from a controller is :
#RequestMapping(...)
public String method(Principal principal, ...) {
Authentication auth = (Authentication) principal;
...
}
Edit (per comment) :
If you do not want to repeat the code on each controller method, you can :
use a #ModelAttribute method to populate the model with the login user in a single place (mainly if you use it in views)
put it in a request scoped bean and directly inject it in controllers through an AOP scoped proxy.