I am building an application where authentication is done by external system. For CSRF handling, I would like to use Spring Security's CSRF suport (http://docs.spring.io/spring-security/site/docs/3.2.0.CI-SNAPSHOT/reference/html/csrf.html)
I tried various options but could not use Spring Security's CSRF support without authentication.
Is it possible to use only CSRF support from Spring Security? (I do not want to use authentication/authorization)
I also had the same requirement as you , and I was able to achieve it by minimal configurations in spring-security.xml
<authentication-manager />
<http create-session="never" use-expressions="true">
<csrf />
<http-basic />
</http>
Here the < authentication-manager /> declares that the spring security doesn't expect an authentication/authorization mechanism, and assumes that all URLs inside the application are already authenticated.
After this add the Spring Security Filter on your web.xml file. This ensures that all requests first pass through spring security mechanism before being handled by the application controller.
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Then in your JSPs (preferably the Header JSP), include the Spring Security's Taglib to access and store the CSRF tokens in the meta tag.
<%# taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
<sec:csrfMetaTags />
<script type="text/javascript">
var csrfHeader = $("meta[name='_csrf_header']").attr("content");
var csrfToken = $("meta[name='_csrf']").attr("content");
</script>
After this include the CSRF Tokens in all your Ajax calls. If not included, you will get the 403 - Access Denied Error.
For Example, if you are using jQuery for doing ajax calls, then you can configure it globally to include the CSRF tokens in the Request Header.
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(csrfHeader, csrfToken);
});
JAR files required for this to work are:
spring-security-acl-5.0.7.RELEASE.jar
spring-security-config-5.0.7.RELEASE.jar
spring-security-core-5.0.7.RELEASE.jar
spring-security-taglibs-5.0.7.RELEASE.jar
spring-security-web-5.0.7.RELEASE.jar
Related
I have a demo JAX-RS project using Jersey. Now I am trying add Spring Security's method level security but unfortunately its not working although intercept-url xml way is working fine.
Added all the dependency in my pom.xml
Updating web.xml as
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/security.xml,
/WEB-INF/beans.xml
</param-value>
</context-param>
<!-- this is default security impl name used by deletetingFiterProxy -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Updating /WEB-INF/security.xml
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<!-- kind of authentication applied 1) Basic 2) form-based etc.. auto-config="true" use-expressions="true"-->
<http auto-config="true">
<http-basic />
</http>
<!-- this allow to enable security annotations in restful resoruces -->
<global-method-security secured-annotations="enabled" />
<!-- for defining users and roles -->
<authentication-manager>
<authentication-provider>
<user-service>
<user name="admin" password="admin" authorities="ROLE_CUSTOMER,ROLE_ADMIN"/>
<user name="student" password="student" authorities="ROLE_CUSTOMER"/>
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
Annotating service inteface methods
public interface StudentServiceInterface {
#GET
#Path("/students")
#Secured("ROLE_CUSTOMER")
public Response getStudents();
#GET
#Path("/students/{id}")
#Secured("ROLE_CUSTOMER")
public Response getStudent(#PathParam("id") int id);
#POST
#Path("/students")
#Consumes(MediaType.APPLICATION_JSON)
#Secured("ROLE_ADMIN")
public Response addStudent(Student stu);
}
Now when I try to access the resource student (/student) class it opens without asking password.
http://localhost:3126/securitydemo/webapi/db/students
StudentServiceInterface interface implementation
#Path("/db")
#Produces(MediaType.APPLICATION_JSON)
public class StudentService implements StudentServiceInterface{
static StudentDao data= new StudentDaoImpl();
#Override
public Response getStudents(){
GenericEntity<List<Student>> entity = new GenericEntity<List<Student>>(data.getAllStudents()){};
return Response.ok(entity).build();
}
#Override
public Response getStudent(#PathParam("id") int id){
return Response.ok(data.getStudent(id)).build();
}
#Override
public Response addStudent(Student stu) {
data.addStudent(stu);
return Response.ok(stu).build();
}
}
You have to use the extention for Spring DI, see Jersey 2.25.1 User Guide:
Jersey provides an extension to support Spring DI. This enables Jersey to use Spring beans as JAX-RS components (e.g. resources and providers) and also allows Spring to inject into Jersey managed components.
The Spring extension module configuration is based on annotations. Spring beans are injected and JAX-RS classes are made Spring managed using annotations. Injected Spring beans can have further dependencies injected using Spring XML configuration. Spring singleton and request scopes are supported.
To enable JAX-RS resources to work Spring functionality that requires proxying, such as Spring transaction management (with #Transactional), Spring Security and aspect oriented programming (such as #Aspect), the resources must themselves be managed by Spring, by annotating with #Component, #Service, #Controller or #Repository:
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import org.springframework.stereotype.Component;
#Component
#Path("/")
public class SomeResource {
#Transactional
#GET
public void updateResource() {
// ...
}
}
Limitations:
Spring beans can't be injected directly into JAX-RS classes by using Spring XML configuration
25.1. Dependencies
If you want to use Jersey Spring DI support you will need to add the jersey-spring3 module into the list of your dependencies:
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring3</artifactId>
<version>2.25.1</version>
</dependency>
The above module adds transitive dependencies on Spring modules. See jersey-spring3 module dependencies for more details about list and scope of dependencies. Please note the module depends on The Spring/HK2 Bridge that is used to inject Spring services into HK2 services or inject HK2 services into Spring services.
I am using spring 4.2.1 with spring security 4.0.2
On login, I need to return a json object tree to the client, containing the cached data it requires for the session.
So I've added a the following method:
#RequestMapping(value = "/login", method = RequestMethod.POST)
public #ResponseBody ServerResponse<?> login(#RequestBody LoginRequest loginRequest, HttpServletRequest request, HttpServletResponse response) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());
Authentication result = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(result);
Object data = null; // Do stuff here
return new ServerResponse<>(data);
}
My spring security config:
<ss:http auto-config="false" use-expressions="true" entry-point-ref="authenticationEntryPoint">
<ss:anonymous enabled="false" />
<!-- this is enabled by default in spring 4 -->
<ss:csrf disabled="true" />
<ss:custom-filter position="FORM_LOGIN_FILTER" ref="myAuthFilter" />
<ss:session-management session-authentication-strategy-ref="sas" />
<ss:port-mappings>
<ss:port-mapping http="8080" https="8443" />
</ss:port-mappings>
<ss:intercept-url pattern="/app/logi**" access="permitAll()" />
<ss:intercept-url pattern="/app/logou**" access="permitAll()" />
<ss:intercept-url pattern="/app/**" access="hasAuthority('user')" />
<ss:intercept-url pattern="/www/**" access="hasAuthority('user')" />
</ss:http>
All the pages I find regarding a programmatic login confirm that what I am doing is fine.
However, when I try and call another web service method later, I get 403 as the client is not logged in.
I read some vague references to having to use a spring filter, but I am not sure how I would get the filter to return the json tree to the client after successful login.
Any suggestions or links to an example on how to do this would be much appreciated.
Thanks
Sooo it turns out the problem was that I was doing Cross Origin Resource Sharing and the browser was not sending the cookie across with the next request.
Basically I was calling the server from html on the file system (with origin file://)
I was handling options calls, but I was not sending back
Access-Control-Allow-Credentials true
headers in the responses and I had to configure angular to send the cookie by passing the flag
withCredentials: true
in the config object to $http.post
my spring mvc application uses the following url
http://xyz.com/<war name>/springmvc/login
I have updated my web.xml
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/springmvc/*</url-pattern>
</filter-mapping>
I updated spring security xml file as follows
<http access-decision-manager-ref="accessDecisionManager" auto-config="true">
<intercept-url pattern="/springmvc/welcome*" access="ADMIN" />
<form-login login-page="/springmvc/login" default-target-url="/springmvc/welcome"
authentication-failure-url="/springmvc/loginfailed" />
<logout logout-success-url="/springmvc/logout" />
<remember-me data-source-ref="dataSource"/>
</http>
If i type springmvc/login or springmvc/welcome it goes to login page. but when I enter the username and password, I get a 404. The url changes to http://xyz.com/UserInterface/springmvc/j_spring_security_check. I expect to see hello.jsp as per the controller below
my login controller is as follows
#RequestMapping(value="/welcome", method = RequestMethod.GET)
public String printWelcome(ModelMap model) {
User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String name = user.getUsername();
model.addAttribute("username", name);
model.addAttribute("message", "Spring Security login + database example");
//logical view name
return "hello";
}
The default login-processing-url property value for form-login is /j_spring_security_check.
i.e. the login form will be posted to this url.
If you context root is UserInterface the default url would be http://xyz.com/UserInterface/j_spring_security_check, but you are creating the url as http://xyz.com/UserInterface/springmvc/j_spring_security_check which spring-security is not able to understand.
You have two options
Change you springSecurityFilterChain url pattern to /* from
/springmvc/*, and make sure that your custom-login form has action
as //j_spring_security_check
Add custom login-processing-url path in form-login
My application requirements are that I need to parse some information from the http request url in order to authenticate the user. Obviously I just cannot use an implementation of UserDetailsService.
My question is, how can implement a UserDetailsService (or equivalent authentication scheme) that needs access to the HttpServletRequest?
My Spring Security version is 3.0.7.RELEASE
There's a very similar question in the Spring Security FAQ.
You can inject a custom AuthenticationDetailsSource into the authentication filter to extract additional relevant information from the incoming request. This information can then be obtained from the submitted Authentication object in a custom AuthenticationProvider.
One of possible solutions is to use RequestContextFilter. You can define it in web.xml as on the following snippet:
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
or if you just need it for some security issues then the better place is to put it to Spring Security config file, as on the following example:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<http>
<custom-filter ref="requestContextFilter" before="FORM_LOGIN_FILTER"/>
<form-login login-page="/login" authentication-failure-url="/login?error=failed" />
</http>
<beans:bean id="requestContextFilter" class="org.springframework.web.filter.RequestContextFilter"/>
<authentication-manager alias="authManager">
<authentication-provider ref="authProvider" />
</authentication-manager>
<beans:bean id="authProvider" class="my.company.CustomAuthProvider" />
</beans:beans>
Then you can use RequestContextHolder.currentRequestAttributes() method in Spring Security classes. For example as follows:
public class CustomAuthProvider extends DaoAuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
System.err.println(attr.getRequest().getParameter("myParameterName"));
return super.authenticate(authentication);
}
}
You'll need to make the servlet a Spring bean as described here.
I am using spring security with jsf 2. I have a filter that control if db access is ok in each page. :
public void doFilter(ServletRequest aReq, ServletResponse aResponse, FilterChain aChain) throws IOException,
ServletException
{
...
if(!myContext.isdbRunning())
{
mLogger.debug("System not working. Redirecting to: "+"/error.jsf");
aReq.setAttribute("errorMsj", "DB is not started. Please contact DB admin.");
aReq.getRequestDispatcher("/error.jsf").forward(aReq, aResponse);
return;
}
aChain.doFilter(aReq, aResponse);
return;
}
If everything is ok, my jsf page is rendered correctly. but when filter finds a problem in db, it processes to an error page.
aReq.getRequestDispatcher("/error.jsf").forward(aReq, aResponse);
but that page dosn't show images and other css based stuff..
does spring security take control and disallow my page contents? or do I have a mistake? How can I solve it? Can I use Phase listener?
Edit: part of my web.xml is
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter>
<filter-name>Gatekeeper</filter-name>
<filter-class>com.jsfsample.filter.GateKeeperFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Gatekeeper</filter-name>
<url-pattern>*.jsf</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Are your css/scripts/images loaded with a separate request ?
If so make sure their url (http://domain.com/styles.css) is not secured.
A bit more detail on unsecuring specific URLs.
In your security context config file you should have something like:
<http auto-config="false" use-expressions="true" entry-point-ref="authenticationEntryPoint">
<intercept-url pattern="/something/relativeUrlThatLoadsImages.jsf" filters="none" />
<!-- OR -->
<intercept-url pattern="/something/relativeUrlThatLoadsImages.jsf" access="IS_AUTHENTICATED_ANONYMOUSLY" />
</http>
Either filters="none" or access="IS_AUTHENTICATED_ANONYMOUSLY" will unsecure the relative URL specified in the pattern attribute.
I personally prefer using filters="none", because it tells spring not to load the filter chain at all for these URLs.
This way you won't need to code to make spring ignore these URLs and you will have a place to change access to them easily in the future if you need to.