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 am very much new to web services. I have exposed some REST services using Jersey 2 in integration with Spring. Now I need to secure those rest services using authentication with username/password. I am told not to use Spring Security.
I have no idea of how to do this. I did search on the net but various links show various implementation and I am unable to decide how to proceed with it.
A common way for authenticating with username and password is to use Basic Authentication. Basically the client needs to send a request header Authorization, with the the header value as Basic Base64Encoded(username:password). So is my username is peeskillet and my password is pass, I, as a client, should set the header as
Authorization: Basic cGVlc2tpbGxldDpwYXNz
In a servlet environment, the container should have support for Basic authentication. You would configure this support on the web.xml. You can see an example in 48.2 Securing Web Applications of the Java EE tutorial. You will also notice in an example
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
That is for SSL support. This is recommended for Basic Authentication.
If you don't want to deal with the hassle of working with security domains and login modules, realm, and such, that would be required to customize the servlet support, or if you're just not in a servlet environment, implementing Basic Auth in a ContainerRequestFilter is really not too difficult.
You can see a complete example of how this could be done at jersey/examples/https-clientserver-grizzly. You should focus on the SecurityFilter
The basic flow in the filter goes something like this
Get the Authorization header. If it doesn't exist, throw an AuthenticationException. In which case the AuthenticationExceptionMapper will send out the header "WWW-Authenticate", "Basic realm=\"" + e.getRealm() + "\", which is part of the Basic Auth protocol
Once we have the header, we parse it just to get the Base64 encoded username:password. Then we decode it, then split it, then separate the user name and password. If any of this process fails, again throw the WebApplicationException that maps to a 400 Bad Request.
Check the username and password. The example source code just checks if the username is user and the password is password, but you will want to use some service in the filter to verify this information. If either of these fail, throw an AuthenticationException
If all goes well, a User is created from the authenticate method, and is injected into an Authorizer (which is a SecurityContext). In JAX-RS, the SecurityContext is normally used for authorization`.
For the authorization, if you want to secure certain areas for certain resources, you can use the #RolesAllowed annotation for your classes or methods. Jersey has support for this annotation, by registering the RolesAllowedDynamicFeature.
What happens under the hood is that the SecurityContext will be obtained from the request. With the example I linked to, you can see the Authorizer, it has an overridden method isUserInRole. This method will be called to check against the value(s) in #RolesAllowed({"ADMIN"}). So when you create the SecurityContext, you should make sure to include on the overridden method, the roles of the user.
For testing, you can simply use a browser. If everything is set up correctly, when you try and access the resource, you should see (in Firefox) a dialog as seen in this post. If you use cURL, you could do
C:/>curl -v -u username:password http://localhost:8080/blah/resource
This will send out a Basic Authenticated request. Because of the -v switch, you should see all the headers involved. If you just want to test with the client API, you can see here how to set it up. In any of the three cases mentioned, the Base64 encoding will be done for you, so you don't have to worry about it.
As for the SSL, you should look into the documentation of your container for information about how to set it up.
So this is really a matter what you would like to achieve. My case was to get this thing running with mobile and a One-Page-App JavaScript.
Basically all you need to do is generate some kind of header that value that will be needed in every consecutive request you client will make.
So you do a endpoint in which you wait for a post with user/password:
#Path("/login")
public class AuthenticationResource {
#POST
#Consumes("application/json")
public Response authenticate(Credentials credential) {
boolean canBeLoggedIn = (...check in your DB or anywher you need to)
if (canBeLoggedIn) {
UUID uuid = UUID.randomUUID();
Token token = new Token();
token.setToken(uuid.toString());
//save your token with associated with user
(...)
return Response.ok(token).type(MediaType.APPLICATION_JSON_TYPE).build();
} else {
return Response.status(Response.Status.UNAUTHORIZED).build();
}
}
}
Now you need to secure resource with need for that token:
#Path("/payment")
#AuthorizedWithToken
public class Payments {
#GET
#Produces("application/json")
public Response sync() {
(...)
}
}
Notice the #AuthorizedWithToken annotation. This annotaation you can create on your own using special meta annotation #NameBinding
#NameBinding
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface AuthorizedWithToken {}
And now for the filter that implements checking of the header:
#AuthorizedWithToken
#Provider
public class XAuthTokenFilter implements ContainerRequestFilter {
private static String X_Auth_Token = "X-Auth-Token";
#Override
public void filter(ContainerRequestContext crc) throws IOException {
String headerValue = crc.getHeaderString(X_Auth_Token);
if (headerValue == null) {
crc.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Missing " + X_Auth_Token + " value").build());
return;
}
if(! TOKEN_FOUND_IN_DB) {
crc.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("Wrong " + X_Auth_Token + " value").build());
return;
}
}
}
You can create any number of your own annotations checking for various things in the http request and mix them. However you need to pay attention to Priorities but that actually easy thing to find. This method needs using https but that is obvious.
Security comes in two main flavours :
Container Based
application based
the standard way to secure spring applications is to use Spring Security (formerly Acegi).
It would be interesting to know why you're not being allowed to use that.
You could use container based security, but I'm guessing that your use of spring precludes that option too.
Since the choice of Spring is usually to obviate the need for the use of a full J2EE container (Edit : though as pointed out below by others, most ordinary servlet containers do allow you to implement various container based security methods)
This really only leaves you with one option which is to roll your own security.
Your use of Jersey suggests that this might be a REST application.
In which case you really ought to stick with standard HTTP Authentication methods that
comes in the following flavours in reverse order of strength :
BASIC
Digest
Form
Certificate
REST applications are usually supposed to be 'stateless', which essentially rules out form based authentication (because you'd require the use of Session)
leaving you with BASIC, Digest and Certificate.
Your next question is, who am I authenticating. If you can expect to know the username AND the password of the user based on what URL they requested (say if it's one set of credentials for all users) then Digest is the best bet since the password is never sent, only a hash.
If you cannot know the Password (because you ask a third party system to validate it etc.) then you are stuck with BASIC.
But you can enhance the security of BASIC by using SSL, or better yet, combining BASIC with client certificate authentication.
In fact BASIC authentication over HTTPS is the standard technique for securing most REST applications.
You can easily implement a Servlet Filter that looks for the Authentication Header and validates the credentials yourself.
There are many examples of such filters, it's a single self contained class file.
If no credentials are found the filter returns 401 passing a prompt for basic auth in the response headers.
If the credentials are invalid you return 403.
App security is almost an entire career in itself, but I hope this helps.
As the former posts say, you could go with different options, with a varying overhead for implementation. From a practical view, if you're going to start with this and are looking for a comfortable way for a simple implementation, I'd recommend container-based option using BASIC authentication.
If you use tomcat, you can setup a realm, which is relatively simple to implement. You could use JDBCRealm, which gets you a user and password from specified columns in your database, and configure it via server.xml and web.xml.
This will prompt you for credentials automatically, everytime you are trying to access your application. You don't have any application-side implementation to do for that.
What I can tell you now is that you already did most of the 'dirty' job integrating Jersey with Spring. I recommend to you to go an Application-based solution, is it does not tie you to a particular container. Spring Security can be intimidating at first, but then when you tame the beast, you see it was actually a friendly puppy.
The fact is that Spring Security is hugely customizable, just by implementing their interfaces. And there is a lot of documentation and support. Plus, you already have a Spring based application.
As all you seek is guidance, I can provide you with some tutorials. You can take advantage from this blog.
http://www.baeldung.com/rest-with-spring-series/
http://www.baeldung.com/2011/10/31/securing-a-restful-web-service-with-spring-security-3-1-part-3/
Hi I am having trouble understanding how to properly use
#Security.Authenticated(Secured.class)
statement within the PlayFrameWork.
I am trying to make sure that only authenticated users can access their accounts. Following from the example provided in the Play docs , it appears that their authentication allows a single user to access every users' account once logged in - rather than just their own.
Normally I would have assumed that you simply get the session value within the action say,
public static Result viewAccount(String account) {
//get session value and check against account name
}
However the Docs use another route:
They define a class
public class Secured extends Security.Authenticator {
#Override
public String getUsername(Context ctx) {
return ctx.session().get("username");
}
#Override
public Result onUnauthorized(Context ctx) {
return redirect(routes.Application.login());
}
}
Apparently now simply using the statement:
#Security.Authenticated(Secured.class)
before an action insures that it is authenticated. But by my understanding and testing this does not block users from logging in to anyones account as it passes simply if the a session value exists - and not if it matches.
How to fix this?
Should I just directly compare the session value? What is the purpose then of
#Security.Authenticated(Secured.class)?
Thanks
(Edit)
to clarify:
I want to allow users to be authorised only to see their own accounts and not others.
So when the statement
#Security.Authenticated(Secured.class)
is used, I would like it to not only check for the presence of a session id but check that it matches an account
Maybe I don't understand your question, but in the documentation you see how to create a login form and actually perform the authentication. Only after that happens does the email session value exist. If that value is in the session, then the user must have logged in with the proper credentials and must have been authenticated.
Of course, if User A knows the credentials of User B, nothing can help that.
If instead you are concerned about authorization, deciding who can see what upon authentication, then you can do a lot of things including leverage OAuth support in Play proper or a plugin like this one.
I have a spring-mvc application that currently has two channels - web application and a REST service. Both have user's http session and I can easily get the "current user" in my service classes.
Now I need to develop another REST service where there are no http sessions and the current user depends on a request parameter. So the controller would read that request parameter and would find the current user.
Now I either need to:
1. modify my service layer methods to accept current user as parameter
or
2. just modify the class that gets the current user from the http session.
I also have the requirement to create an audit log and I'm going to use Spring AOP for that. The Aspect will need access to the "current user" too. So option #1 probably won't work for me and I will go with #2.
For option #2 I'll create an interceptor that will put the current user in a ThreadLocal variable. The controller for the new REST service will do the same and then in my service layer and in the audit log aspect I can get the current user from there.
I haven't done anything like this before and was wondering if there is a better approach. Or what kind of issues I should expect with this approach.
I will appreciate any comments and ideas.
Oz
Here is how I currently get the current user:
#Override
public User getCurrentUser()
{
Authentication currentUser = getAuthentication();
return userService.getByLoginName(currentUser.getName());
}
protected Authentication getAuthentication()
{
return SecurityContextHolder.getContext().getAuthentication();
}
I think a simple way to do what you need is to configure a servlet filter for your new REST webservice to populate the SecurityContextHolder with an Authentication object build from request parameters.
You can read this : http://static.springsource.org/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#d0e2171 for more details.
With this solution you don't need to modify the code to retrieve the current user. (note that the SecurityContextHolder is already using a ThreadLocal to store the SecurityContext and so the Authentication)
How to check the userdetails before spring security authentication is takes place.
Here in my project during login i need to check the basic validations username/password exists , validity of username , max retry limit(5invalid entries after that block a/c) and show error based on the failure.
On correct username/password need to check whether user logging into application for first time (based on flag in db) ,if so need to display an disclaimer page[ to access the application the user should accept the disclaimer].
Here is the flow
username/pwd in form -> Submit ->check for valid credentials
--> No --> display error[update retry flag]
--> yes -->check validity-->exceed->display error
-->with in validity period-->check is first time(and disclaimer accepted) -->show app if disclaimer already accepted else show disclaimer page without logging into application
On I accept button login the user to application.
I don't think this will involve Spring security per se. In your action class just call a method that checks the DB. If they have been logged in before return to an action that redirects the user to whatever page is appropriate. If they have not, then send them to the disclaimer page.
When they submit the form for the disclaimer page, check for acceptance. If accepted, then update the DB and redirect them to the appropriate page. If not, display an error.
If I recall Spring security correctly, the acceptance page and the login page, etc. will not be secured, but other pages will be. So, like I said, I'm not sure that Spring is really involved. It is just a matter of correctly routing via your Struts actions which do all the work of looking up in the DB and routing.
Basically, spring security takes care of it - but you can override it.
This is just a simple example how i would do it:
#Autowired
private AuthenticationManager authenticationManager;
#RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<?> login(#RequestBody AuthenticationRequest authenticationRequest, Device device) throws AuthenticationException {
// Perform the security
final Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
authenticationRequest.getUsername(),
authenticationRequest.getPassword()
)
); ...
}
AutheticationRequest is just a simple object with 2 Strings inside (username, password)
Bare in mind that this method throws AuthenticationException but you can also catch it in try/catch and do some logic on failed login.
Hope it helps.