Short Version
Is javax.servlet.ServletRequest's method setAttribute(<key>, <Object>) only used as a means of passing objects between methods in Java code?
Long version
Let's say I have a javax.servlet.Filter implementation to handle all logged in users' authentication using cookies:
in Spring Boot
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class AuthFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
Cookie[] cookies = null;
if (request instanceof HttpServletRequest) {
cookies = ((HttpServletRequest) request).getCookies();
}
Optional<User> user = mySessionAuthMethod(cookies);
if (user.isPresent()) {
request.setAttribute("user", user.get());
}
chain.doFilter(request, response);
}
}
Then later, I can avoid manual authentication in all of the Web API methods, and just check the user attribute. Example of a #RestController's method:
#RequestMapping(value = "/profile")
#CrossOrigin(origins = {MyProperties.ORIGIN}, allowCredentials = "true")
public ResponseEntity getProfile(HttpServletRequest request, HttpServletResponse response) {
String user = request.getAttribute("user");
if (user != null) {
return myGetProfileResponse(user);
}
return myNotLoggedInResponse();
}
My questions are:
Is this form of authentication secure? What I mean is, are the attributes in the ServletRequest only added and used in Java for communication between methods, or could they be added to the request already before reaching the server?
Is this way of authentication using Filters a good practice to avoid duplicate code?
Additional Explanation
The real reason of doing this is not only authentication. I have also Filters which need to process each and every request and pass objects to the Controllers. What I definitely want is that none of these objects and information can be forged even by a person who knows the implementation of the system.
I think I have found the answer from the documentation of getAttribute
Attributes can be set two ways. The servlet container may set attributes to make available custom information about a request. For example, for requests made using HTTPS, the attribute javax.servlet.request.X509Certificate can be used to retrieve information on the certificate of the client. Attributes can also be set programatically using ServletRequest#setAttribute. This allows information to be embedded into a request before a RequestDispatcher call.
So according to this (if there is no missing information), it should be completely safe to pass custom objects and know that they were always created by the server.
Related
In my spring project, I would like to create an endpoint which returns the security context user details for a clients active session cookie.
One way would be a specific method implementation within a controller
#RequestMapping(value = "/session", method = GET)
public AuthenticatedUserDto getCurrentSession(HttpServletResponse response) {
if (SecurityContextHolder.getContext().getAuthentication().getPrincipal() != null && SecurityContextHolder.getContext().getAuthentication().getPrincipal() instanceof User) {
return AuthenticatedUserBuilder.build(SecurityContextHolder.getContext().getAuthentication());
}
throw new BadCredentialsException("unkown session");
}
There is few things which bother me on this approach:
I need a ugly if to determine if it's not an anonymous authentication
I am handling this issue far to deep within the context, as I have all information already as soon the session cookie gets resolved.
So my other approach is using a security chain filter for matching the specific url ("/session), and handle the task there.
public class SessionObjectResponder extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
...//do create my response
..
}
}
Adding the filter just before the AnonymousAuthenticationFilter, should guarantee that I would have the security context available matching the session cookie. So I configured my web security like this:
httpSecurity.antMatcher("/session").addFilterBefore(new SessionObjectResponder(),AnonymousAuthenticationFilter.class);
Oddly, the SecurityContextHolder contains a null authentication even when a valid session cookie is passed.
I also can see that within the SecurityContextPersistenceFilter no security context gets set.
When I remove the httpSecurity configuration for this filter and add the #Component to the filter class, the SecurityContextis loaded correctly again. (Without path matching of course)
Why is that?
You can ask for the Authentication or Principal like this:
#RequestMapping(value = "/session", method = GET)
public AuthenticatedUserDto getCurrentSession(Authentication auth) {
if (auth == null || !auth.isAuthenticated())
throw new BadCredentialsException("unkown session");
return AuthenticatedUserBuilder.build(auth);
}
When you use #Component or #Service or #Controller ... then it becomes spring managed been. Their life cycle is managed by spring container. Therefore context is available in container created instances.
But in your case of
httpSecurity.antMatcher("/session").addFilterBefore(new SessionObjectResponder(),AnonymousAuthenticationFilter.class);
You are creating the instance with new operator. It is not created by the container. So this instance is outside the container scope. Therefore the context is not available in that instance at runtime.
While working on a Java Servlet Filter to integrate with an internal SSO I struggled to find the correct way to create a persistent login using programmatic security functions. Specifically, after reading many tutorials such as this I was calling request.login(username,password) and a user Principal was created that returned correct values for methods request.isUserInRole(role) etc. for the current request.
I expected this to be persistent for the user, but subsequent requests would return null for request.getUserPrincipal() so my authentication code was entered on every request. What I found here after much searching was that calling request.getSession(true) before calling request.login(username,password) causes the Principal to be persistent for the user session.
Is it required to explicitly create a session before logging the user in to persist the Principal for the session? This behaviour seems odd to me so I am wondering if I am doing something wrong. I appreciate any insight from developers more knowledgeable on the matter than myself. Using JDK 7 and Tomcat 8. Realm is an extension of RealmBase that overrides authenticate(username,password), getName(), getPassword(username) and getPrincipal(username).
public void doFilter(ServletRequest req, ServletResponse res, FilterChain fc) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
if(request.getUserPrincipal() == null) {
try {
String userName = request.getParameter(userNameField);
String password = request.getParameter(passwordField);
request.getSession(true); // <-- block always entered without this
request.login(username,password);
}
catch(ServletException e) {
// Handle failed login...
return;
}
}
fc.doFilter(req,res);
}
The answer is in the Tomcat source for AuthenticatorBase.java where the login(username,password) chaining calls the configured Realm's authenticate(username,password) method and then calls register(...) passing the returned Principal and stores it in a session if one exists.
Our application is using Wicket. We're using Wicket's own forms to handle authentication, the main benefits being that the look of the site is kept consistent.
We thought we couldn't do container authentication because our application allows the user to switch authentication mechanisms mid-stream, and Jetty itself was creating quite a bit of friction anyway, just getting simple authentication to work at the container level.
So we ended up implementing authentication via a filter (there are a number of good examples out there.)
Now I have discovered that by doing this, Wicket authentication is slightly broken. What happened was:
Anonymous user would visit the site.
Security filter determines that the user isn't authenticated and redirects to sign-in.
Wicket renders the sign-in page.
User signs in.
Wicket processes the post to the sign-in form and redirects user back.
Security filter determines that the user isn't authenticated and redirects...
I looked inside my subclass of AuthenticatedWebSession, hoping to find some way I could get a hold of the HttpSession and set a session attribute which could then be checked from the filter. However I couldn't for the life of me find a way to do it.
What I resorted to doing was making yet another filter, coded like this:
public class StealWicketUserFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
// Nothing to initialise.
}
#Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException
{
filterChain.doFilter(servletRequest, servletResponse);
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
if ("POST".equals(httpServletRequest.getMethod()) &&
"/sign_in".equals(httpServletRequest.getRequestURI())) {
HttpSession session = httpServletRequest.getSession();
ServerUser currentUser = (ServerUser)
session.getAttribute("wicket:webUIServlet:currentUser");
if (currentUser != null) {
session.setAttribute(CombinedSecurityFilter.CURRENT_USER_ATTRIBUTE,
currentUser);
}
}
else if ("/sign_out".equals(httpServletRequest.getRequestURI())) {
HttpSession session = httpServletRequest.getSession();
session.removeAttribute(CombinedSecurityFilter.CURRENT_USER_ATTRIBUTE);
}
}
#Override
public void destroy() {
// Nothing to destroy.
}
}
This of course works (and will continue to work until Wicket change the prefix they store session attributes under.)
I guess what I want to know is whether this is a bad idea and whether there is a "proper Wicket way" to do this sort of thing.
As for the reason we don't use Wicket's authentication alone - the reason is that we wanted to support SPNEGO authentication and potentially other external authentication types.
You can get hold of your HttpSession,albeit through Request and not Session.
What you need is:
WebRequest req = (WebRequest)RequestCycle.get().getRequest();
HttpSession session = req.getHttpServletRequest().getSession();
However I'm pretty sure Wicket authentication isn't broken in such an obvious manner so I'd probably try to find out what is causing this glitch instead.
I have written a method for user authentication method in REST web service. After successful authentication, I want to pass the username. How can I pass it? Can I get value pass from login web service method in other web service method.
My code for login is:
#GET
#Produces("application/json")
public Response login(#Context HttpServletRequest req,#Context HttpServletResponse res,#QueryParam("loginname")String loginname,#QueryParam("password")String password) throws IOException, ServletException
{
userDAOImpl impl = new userDAOImpl();
Mongo mongo=impl.getConnection("127.0.0.1","27017");
DB db=impl.getDataBase(mongo,"userdb");
DBCollection coll=impl.getColl(db,"userdb");
userDTO dto = new userDTO();
dto.setUsername(loginname);
dto.setPassword(password);
if(impl.checkUser(coll, dto))
{
mongo.close();
return Response.ok().build();
}
else
{
return Response.status(Response.Status.FORBIDDEN).build();
}
}
I can't tell if you are using some sort of web framework here or not, so I'll answer the question as if you aren't.
Servlets do allow you to add attributes to a request (which are gone after the request is processed), to a page (again, lost when the page is gone), or session (which lives as long as your browser/servlet maintain the session).
I'd suggest you start here with a simple example of how to deal with servlet attributes and paramters. And here's a more detailed explaination.
I need to grab a certain custom HTTP header value from every request and put it in WebSession so that it will be available on any WebPage later on. (I believe the Wicket way to do this is to have a custom class extending WebSession that has appropriate accessors.)
My question is, what kind of Filter (or other mechanism) I need to be able to both intercept the header and access the WebSession for storing the value?
I tried to do this with a normal Java EE Filter, using
CustomSession session = (CustomSession) AuthenticatedWebSession.get();
But (perhaps not surprisingly), that yields:
java.lang.IllegalStateException:
you can only locate or create sessions in the context of a request cycle
Should I perhaps extend WicketFilter and do it there (can I access the session at that point?), or is something even more complicated required?
Of course, please point it out if I'm doing something completely wrong; I'm new to Wicket.
I'd guess you need to implement a custom WebRequestCycle:
public class CustomRequestCycle extends WebRequestCycle{
public CustomRequestCycle(WebApplication application,
WebRequest request,
Response response){
super(application, request, response);
String headerValue = request.getHttpServletRequest().getHeader("foo");
((MyCustomSession)Session.get()).setFoo(headerValue);
}
}
And in your WebApplication class you register the custom RequestCycle like this:
public class MyApp extends WebApplication{
#Override
public RequestCycle newRequestCycle(Request request, Response response){
return new CustomRequestCycle(this, (WebRequest) request, response);
}
}
Reference:
Request cycle and request cycle
processor