In our Java based web application we have the concept of Roles and Users. Basically we have many links in our page, and these links can be accessed based on the Role assigned to the user.The Roles and User related information is stored in DB.
During the User Login time we query the DB, get all the related roles and display only the links that are assigned to his Roles. So the user can see only the links he has access.
This way is Working Very Weel. But every time user login the program checks his role level from database.this is burden to database. Please suggest another ways we prevent the accessing multiple links in Web Application.
2 options:
1) Put the user roles in the session when he is authenticated for the first time and access the session everytime he tries to access a functionality
2) Use a Map<String, List<Role>> as a cache and access this map instead of the Database for authenticating a user. Key of the map can be the username. When the user logs in, add the user to the map. When the user logs out or the session expires, remove from the map. Spring Security uses Ehcache for caching so you could do the same.
public void doLogin(HttpSevletRequest request) {
String username = request.getParameter("username");
String password = request.getParameter("password");
User user = service.authenticate(username, password);
HttpSession session = request.getSession();
session.setAttribute("roles", user.getRoles());
}
public void save(HttpServletRequest request) {
List<Role> roles = request.getSession().getAttribute("roles");
for(Role role : roles) {
if(role.getName()=="save") {
service.save();
break;
}
}
}
A trivial example added. For the cache, it'll be a similar except instead of session use Map.
Related
Team
Using Spring boot I am able to accomplish workflows, where oAuth server can generate the token for the logged-in user. However, how does the oAuth server generate a unique token for each anonymous user?
For e.g. if 2 concurrent users are trying to access the resource server from their respective browsers, then I would like the resource server to identify the 2 different users in each subsequent requests that they make. For that, I would like to generate different tokens for each anonymous user. Is that possible and if yes then how?
It is simple thing, you are making it complex.
I would like to generate different tokens for each anonymous user. Is that possible
Once you generate valid token, user will become authenticated user.
You might think about validating token and setting ROLE='ROLE_ANONYMOUS'. As I told it will be like making simple thing to complex. Making authenticated user to Anonymous user is not good approach. You can create a new role like ROLE_SEMIANONYMOUS and grant authorities which were open for ANONYMOUS user.
(but this approach doesn't make any sense for me so explaining alternative to achieve your requirement of identifying AnonymousUser)
As "Anonymous User = UnAuthenticated User".
For anonymous user if you print principal
String principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
principal = "anonymousUser"
You can create a session for anonymous user for identifying him as given below
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Object principal = authentication.getPrincipal();
if(principal instanceof String && ((String)principal).equalsIgnoreCase("anonymousUser"))
{
if(request.getSession(false) == null)
{
HttpSession session = request.getSession();
session.setMaxInactiveInterval(300); // As per Your requirement
// store session id from session and ip address from request in to DB or cache if required and use it
}
else
{
//identify anonymous user against session id and stored details
}
}
You can achieve this by registering a filter
In convetional spring order of filter should be after springSecurityFilterChain.
In spring boot you can achieve it by FilterRegistrationBean
How can I easily create spring user sessions based on information found in a mySQL database connected with JPA ?
As a simple example lets say I have 2 tables:
users
INT id
VARCHAR(30) username
VARCHAR(20) password
bookmark
INT id
VARCHAR(20) name
TEXT desctription
int user (FK)
Users will be able to view their bookmarks by requesting the following URL:
http://localhost:8080/bookmarks
In this manner I need separate user sessions (authentication not my main priority in this case) to be able to show bookmarks specific to the user.
Another way to go is by accessing bookmark information through:
http://localhost:8080/{userId}/bookmarks
In this case how can I prevent users from accessing another users' bookmark information ? (Such as preventing user id 1 from accessing user id 2s' bookmarks by using the url http://localhost:8080/2/bookmarks
If you would use Spring Security to secure your app, you can create custom UserDetailsService to read user data for each request from DB. Something like this:
#Component
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
protected UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String email)
throws UsernameNotFoundException {
User user = userRepository.findByEmail(email);
if (user == null) {
throw new UsernameNotFoundException(String.format("User with email=%s was not found", email));
}
return user;
}
}
Of course assuming that you have JPA entity called User implementing UserDetails interface.
With this mechanism you can inject User instance into MVC controllers:
#GetMapping("/bookmarks")
public List<Bookmark> readBookmarks(Principal principal) {
User user = (User) principal;
// read bookmarks code
}
You can also read in anywhere in the app via:
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
User user = (User) principal;
REACTION ON COMMENT:
It is broad topic with a lot of decisions to make. e.g. you may consider using JWT, OAUTH2 or token based based authentication. If you are starting with Spring Security, I recommend to look at their Guides section. Especially Spting MVC guide is relevant for you.
I am trying to create a simple login page. I retrieve a User object from my database using hibernate. That part works fine, I'm doing that as follows:
//data from login form
String username = request.getParameter("username").trim();
String password = request.getParameter("password").trim();
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
try {
User currentUser = (User) session.get(User.class, username);
if(password.equals(currentUser.getPassword())) {
response.sendRedirect("index.jsp?page=login&success=true");
} else {
session.getTransaction().rollback();
response.sendRedirect("index.jsp?page=login&success=false");
}
} catch //...
Given the correct credentials, login is successful. If I understand correctly, my code above already stores the User in the session, if the login was successful, so all I have to do is access the session?
However I can't figure out how to access the retrieved User object from the session in other places of my website. After the user is logged in, I want to show user-specific information on my website and for that, I need to check the username and whether the user is logged in at all.
So to sum up: How can I use the retrieved User object in other parts of my website?
I just started learning Java EE and hibernate, so please bear with me.
You can do it using an HttpSession that can be retrieved by the HttpServletRequest object.
HttpSession httpSession = request.getSession();
httpSession.setAttribute("user", user);
Now to check if the user object is present in different parts of your application, you can do the following:
HttpSession httpSession = request.getSession(false);
//False because we do not want it to create a new session if it does not exist.
User user = null;
if(httpSession != null){
user = (User) httpSession.getAttribute("user");
}
if(user!=null){
// Do stuff here
}
To logout a user or in other words, to invalidate the session, you can call the invalidate method.
httpSession.invalidate();
Useful links: HttpServletRequest and HttpSession
HttpSession is different from the Hibernate session. The Hibernate session provides a way for you to query and save persistent entities that are stored in a database. The HttpSession is provided by the servlet container to give a way to
store objects for a given user based on a cookie provided in the user's request.
What you store in the HttpSession should be minimal, partly to save on overhead from nodes in the cluster reconciling their sessions but mostly to make your application less error-prone. Here it could be sufficient to store a user's ID in the session rather than the whole user object. Even if the User object contained roles it would be better to look those up for each request so that any changes get applied immediately. Also by storing only ids you avoid problems with reattaching entities (allowing you to avoid one of the more confusing and troublesome parts of using Hibernate). When something else in your application needs to access the User it can query the Hibernate session (using session.get(id)) passing in the primary key value stored in the HttpSession.
You should use a 1-way hash to store passwords so that will change how you compare passwords.
The application should create a Hibernate SessionFactory once only upon initialization, it is threadsafe and everything in the application should use that one instance.
Rolling back a transaction where all you do is select seems unnecessary.
Typically you access the HttpSession only from the view and the web controller. It looks like you have web controller logic and business logic lumped together, a division of responsibilities between controller and service may be helpful here.
Assuming you are in a Web application and you want something from the User entity, you should propagate the same value/reference to the Web/controller layer (in case you are using an MVC approach); then keep it there since it's the most appropriate place to store something via the HTTP session provided by most frameworks.
RECOMMENDATION(S)
You should not be rolling back a get/select operation?
A SessionFactory should be instantiated once.
The Session you have mentioned here(org.hibernate.Session) is cannot access from the other places of your web site instead you put your User object into a HttpSession .
Here is how you going do this:
HttpSession httpSession = request.getSession();
httpSession.setAttribute("loggedUser", your_user_object reference_here );
Here is how you access from other placess:
httpSession.getAttribute("loggedUser");//return type is Object here
I'm working on a cloud endpoints backend and want to restrict certain operations to admin users.
My current code works like this:
#ApiMethod(httpMethod = "PATCH", name = "item.update", path = "items")
public Item update(Item newObject, User user)
throws UnauthorizedException, OAuthRequestException {
OAuthService oAuthService = OAuthServiceFactory.getOAuthService();
if (!oAuthService.isUserAdmin()) {
throw new UnauthorizedException("Only admin users can modify content.");
}
...
}
I know app engine has a concept of user roles, but I'm curious if Endpoints
do. I've tried using the OAuthService.isUserAdmin() call but that doesn't
seem to be working out very well and the docs have a big old warning saying
Note: You should not confuse Endpoints auth with the auth for
non-Endpoints App Engine web apps described in the article on configuration settings
https://developers.google.com/appengine/articles/auth in the Admin
Console, where you also specify the user login requirement in your
web.xmlhttps://developers.google.com/appengine/docs/java/config/webxml#Security_and_Authentication
file. That approach is not used with Endpoints."
Do I have to create some sort of authorization myself that uses the User object that's passed into the update method? Any thoughts?
I had similar issues. Indeed OAuth user service has nothing to do with AppEngine user service. What I ended up doing was having a dedicated user type entity in my datastore where I store a specific flag (regular/admin) for each user. This flag is updated when I use AppEngine user service (i.e. so that the administrators I specified in the console get the proper admin flag).
In my endpoints API I get the current user authDomain and id, look up in my datastore to check whether it has the admin flag. The key of my user entity is composed of "authDomain:userId" and as I only support google user for now, it looks like (gmail.com:123456789)
This means that an administrator has to login once using the AppEngine UserService (i.e. a dedicated webpage in my case) so that the flag is properly updated
I needed to do the same thing and validate some endpoint to grant access only to admin members listed in the project console and used the same implementation presented above, but the oAuthService.isUserAdmin() accept one or more string parameters, this parameters are scopes that you specify and the Oauth uses to get user informations, in my case i just set this parameter and it works like the code bellow.
OAuthService authService = OAuthServiceFactory.getOAuthService();
User user;
try {
com.google.appengine.api.users.User currentUser =
authService.getCurrentUser(Constants.EMAIL_SCOPE);
if (currentUser != null && authService.isUserAdmin(Constants.EMAIL_SCOPE)) {
user = new User(currentUser.getEmail());
return user;
}
...
The EMAIL_SCOPE constant is defined by
public static final String EMAIL_SCOPE = "https://www.googleapis.com/auth/userinfo.email";
In my case i implemented an authenticator, to pass user information to endpoint only if it's admin user, you can read more about the authenticators if you want.
https://cloud.google.com/appengine/docs/java/endpoints/javadoc/com/google/api/server/spi/config/Authenticator
I am trying to handle a situation when after an successful authentication with openId provider I discover that there is no account in my db associated with user openId identifier.
Can you tell me how should I handle the situation. Now, I am displaying register form and ask a user for creating an account. However, I have a problem with user authentication status, he is now being seen as authenticated by spring SecurityContext class.
How do I deauthenticate user in my controller action before redirecting to ''register new user page''? Is this approach a good one or should I do it in some other way?
Ok, so separating authentication from authorization as was mentioned in Samuel's post was really helpful. However there are still many gotchas and I found deauthentication still a must because there is no easy way in spring to add to user new roles. So the easiest way is to force user to login again and let spring handle role assignment during login.
In order to deauthenticate user in spring security you have to invoke:
SecurityContextHolder.clearContext();
as an alternative you can throw an exception in your UserDetailsService implementation (see below). It has the downside that you would deauthenticate user and lose user context data so it would be impossible to match new user accout with openid account during process of creating new local account. And you have to match those account after user login with traditional username and password. My solution was to deauthenticate user just after creating new account.
In order to grant user roles(privileges) you have to override UserDetailsService, in case someone find this useful here is my implementation:
public final class MyUserDetailsService implements UserDetailsService {
private final UsersDao usersDao;
#Autowired
public UserDetailsServiceImpl(final UsersDao usersDao) {
this.usersDao = usersDao;
}
#Override
public UserDetails loadUserByUsername(final String username) {
UserEntity user = usersDao.getUserByOpenIdIdentifier(username);
if (user == null) {
// there is no such user in our db, we could here throw
// an Exception instead then the user would also be deuthenticated
return new User(username, "", new ArrayList<GrantedAuthority>());
}
//here we are granting to users roles based on values from db
final Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(user.getUserType().toString()));
final UserDetails result = new User(username, "", authorities);
return result;
}
}
I think that you might be mixing two concepts: authentication and authorization. Authentication is knowing who the user is, authorization is the right to use access a resource of a feature.
In spring security, this two concepts are implemented by the authentication-manager and the access-decision-manager.
The fact that a user does not exist in your database is not a reason to deny him is identity: no deauthentication! But beeing authenticated can be a criterion in the access decision management. Example: the AuthenticatedVoter.
You should not touch at the authentication, but customize the access-decision-manager to apply the following rules:
A user who exists in your database has access to everything except account creation feature
A user who doesn't exist in your database has access only to the account creation feature.
This is all about access management, not authentication.
Read more at http://static.springsource.org/spring-security/site/docs/3.0.x/reference/ns-config.html#ns-access-manager
PS: The documentation is not exhaustive in spring security, but the source code is very readable. My advice is to check it out and look at the implementation of the elements you need to customize.