Reputation:
I'm unable to access Spring Security information during a servlet multipart post. The spring security information is available during regular get and post methods, but is not available for a multipart post method. I tried unsuccessfully to access this security information directly through SecurityContextHolder.getContext().getAuthentication() and through an injected service that accesses SecurityContextHolder.getContext().getAuthentication().
I also implemented an HttpRequestHandler and a ServletWrappingController. Once again, I was able to successuly inject spring beans into them and access Spring Security info for regular get and post methods, but I was not able access Spring Security info for a multipart posts. I know that there are new MultiPart capabilities built into Spring 3.0 but because our website will require full access to the file upload stream I won't be able to use them. For that reason, I am focusing on the HttpServlet, HttpRequestHandler and the ServletWrappingController.
The code I'm posting here is all test code written to solve this specific problem I'm facing with security information not being available during a multipart upload (not meant to be of production quality). It is for an HttpServlet.
Please let me know if there's something I'm doing wrong. Or if not, if there's a workaround or a better way to accomplish a multipart upload with access to Spring Security info while maintaining access to the file upload stream? Any assistance that someone can offer with this problem will be greatly appreciated!
Below is the test servlet code. Comments below as to what works and what doesn't is based on a user logged in to the website using Spring Security 3.1:
//many import statements not displayed
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
public class UploadServlet extends HttpServlet {
public void service(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException {
super.service(req, res);
}
public void init(ServletConfig config) throws ServletException {
super.init(config);
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
config.getServletContext());
}
//The following is always injected and available
//however, it only returns valid security information for regular get and post methods,
//not for multipart post methods
@Autowired
private CustomUserService customUserService;
//The following is always injected and available and always returns the expected data
@Autowired
private GuideService guideService;
//the following does not work when the client issues a multipart post, it does work for non-multipart
public boolean getAuthenticated(){
boolean authorized = false;
for (GrantedAuthority authority : SecurityContextHolder.getContext().getAuthentication().getAuthorities()) {
if(authority.getAuthority().equals("ROLE_USER") || authority.getAuthority().equals("ROLE_ADMIN")) {
authorized = true;
break;
}
}
return authorized;
}
//The following test get method works fine
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if(getAuthenticated()){
PrintWriter out = resp.getWriter();
out.write("<h1>Guide Info</h1><br/>");
Guide guide = guideService.findById(2l);
out.write(guide.getName() + "<br/>");
out.write(guide.getDescription() + "<br/>");
out.write("UserName: " + customUserService.getCurrentUser().getUsername() + "<br/>");
}
else{
PrintWriter out = resp.getWriter();
out.write("<h1>You're not authorized</h1><br/>");
}
}
//This post method
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//the following always works, whether the clients posts using multipart or not
String guideName = guideService.findById(2l).getName();
//the following does not work when the client issues a multipart post, it does work for non-multipart
String userName = customUserService.getCurrentUser().getUsername();
//the following does not work when the client issues a multipart post, it does work for non-multipart
if(getAuthenticated()){
String responseString = RESP_SUCCESS;
boolean isMultipart = ServletFileUpload.isMultipartContent(req);
if (isMultipart) {
ServletFileUpload upload = new ServletFileUpload();
//commmons fileupload code
// Not a multi-part MIME request.
else {
//...
}
//...
}
else{
//...
}
}
}
Here is the relevant portion of web.xml:
<servlet>
<servlet-name>fgm</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/spring/webmvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>fgm</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>com.guides.servlet.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>/upload</url-pattern>
</servlet-mapping>
Upvotes: 7
Views: 1061
Reputation: 1979
I can confirm that Spring 3.0.x and Spring Security 3.0.x together work with multipart posts as well as they work with other types of requests. I've run into similar behavior, and in our case, the security filter wasn't getting applied to the request due to our mistake in the filter mappings.
Can you post the parts of your web.xml that define the security filter, and map it to the desired paths?
Upvotes: 2
Reputation: 1239
It might be worth checking how your client is performing the multi-part post, are you using a different mechanism/library to your standard post?
If I had to guess I would say your client code isn't authenticating correctly for the multi-part use-case.
E.g. Using standard Java for the normal post and Apache libs for the multipart post and forgetting to set the appropriate http headers when using the Apache stuff.
Upvotes: 0
Reputation: 28786
Security details as provided by SecurityContextHolder are (by default) stored in a ThreadLocal.
Does upload servlet creates a new thread to handle multiparts ? Try changing the SecurityContextHolderStrategy to MODE_INHERITABLETHREADLOCAL
Similar issues: How to set up Spring Security SecurityContextHolder strategy?
Upvotes: 0
Reputation: 754
This might help you, if you're using Spring MVC:
{
@RequestMapping(method = RequestMethod.POST, value = "/some/post/url")
public void postFile(MultipartHttpServletRequest request) {
MultipartFile multipartFile = request.getFileMap().get("fileControlName");
...
}
}
Upvotes: 0