#Secured is not working while integrating Spring Security in Jersey project - spring-security

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.

Related

How to keep SecurityContext set through WithSecurityContextFactory in Spring Security tests?

I'm using Spring 4.1.5 and Spring Security 4.0.0.RELEASE.
I read http://spring.io/blog/2014/05/07/preview-spring-security-test-method-security (nice article by Rob Winch) and developed my own implementation of WithSecurityContextFactory to be able to test my Spring MVC controllers:
public class WithMockCustomUserSecurityContextFactory implements WithSecurityContextFactory<WithMockCustomUser> {
#Override
public SecurityContext createSecurityContext(WithMockCustomUser customUser) {
final User fakeUser = new User();
final SecurityUser principal = new SecurityUser(fakeUser);
final Authentication auth = new UsernamePasswordAuthenticationToken(principal, "password", HelpersTest.getAuthorities(customUser.faps()));
final SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(auth);
return context;
}
}
My abstract resource test class is as follow:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations =
{
"classpath:spring/mock-daos-and-scan-for-services.xml",
"classpath:security.xml",
"classpath:singletons.xml",
"classpath:controller-scan.xml",
"classpath:servlet.xml" })
#TestExecutionListeners(listeners=
{
ServletTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
WithSecurityContextTestExcecutionListener.class })
public abstract class AbstractResourceMockMvcTest {
#Autowired
private WebApplicationContext wac;
#Autowired
private Filter springSecurityFilterChain;
private MockMvc mockMvc;
[...]
#Before
public void setup() {
this.mockMvc =
MockMvcBuilders.webAppContextSetup(this.getWac())
.addFilters(springSecurityFilterChain)
.build();
}
[...]
}
Then, my concrete test class inherits from AbstractResourceTest (from above) and it uses the following annotation on a #Test-enabled method:
#WithMockCustomUser(faps={"promotion_read"})
Tracing the code, I can confirm WithMockCustomUserSecurityContextFactory.createSecurityContext() is called and its return value is set in SecurityContextHolder.setContext() (through TestSecurityContextHolder.setContext()).
So far, so good !
However, later in the process, SecurityContextPersistenceFilter.doFilter() calls SecurityContextHolder.setContext() and this overwrites the context set by the test and I lose track of the mocked security context I prepared.
security.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
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-4.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd
"
>
<!-- HTTP security handling -->
<security:http use-expressions="true">
<security:logout logout-url="/j_spring_security_logout" invalidate-session="true" logout-success-url="/login.jsp?loggedout=true" />
<security:custom-filter before="FIRST" ref="multiTenantRequestFilter" />
<!-- make sure following page are not secured -->
<security:intercept-url pattern="/*/*/internal/**" access="hasIpAddress('127.0.0.1')" />
<!-- make sure everything else going through the security filter is secured -->
<security:intercept-url pattern="/resources/**" access="hasRole('ROLE_USER')" requires-channel="any" />
<!-- supporting basic authentication for unattended connections (web services) -->
<security:http-basic />
</security:http>
<!-- authentication strategy -->
<security:authentication-manager alias="authManager">
<security:authentication-provider user-service-ref="userSecurityService">
<security:password-encoder ref="passwordEncoder" />
</security:authentication-provider>
</security:authentication-manager>
<!-- custom filter to intercept the tenant name from the login form -->
<bean id="multiTenantRequestFilter" class="com.meicpg.ti.web.MultiTenantRequestFilter" />
</beans>
servlet.xml:
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
"
>
<mvc:annotation-driven>
<!-- Content skipped for StackOverflow question -->
</mvc:annotation-driven>
<context:annotation-config />
<bean id="annotationExceptionResolver" class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver"></bean>
<security:global-method-security pre-post-annotations="enabled"/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
How can I prevent this security context overwrite ? Does my security.xml contain an obvious flaw I missed ?
PS: I skipped the other context configuration files as they seem irrelevant to the problem.
Thanks in advance !
Unfortunately that blog post is just for method level security and does not have complete instructions for MockMvc setup (the following blog in the series does). Additionally, the blogs are actually dated (I have updated them to reflect that readers should refer to the reference documentation). You can find updated instructions in the Testing Section of the reference.
In short, update your code to the following:
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations =
{
"classpath:spring/mock-daos-and-scan-for-services.xml",
"classpath:security.xml",
"classpath:singletons.xml",
"classpath:controller-scan.xml",
"classpath:servlet.xml" })
public abstract class AbstractResourceMockMvcTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
[...]
#Before
public void setup() {
this.mockMvc =
MockMvcBuilders.webAppContextSetup(this.getWac())
.apply(springSecurity())
.build();
}
#Test
#WithMockCustomUser(faps={"promotion_read"})
public void myTest() {
...
}
[...]
}
A few highlights:
You no longer need to provide the TestExecutionListeners
Use .apply(springSecurity()) instead of adding the spring security filter chain manually
This works because Spring Security's test support i.e. apply(springSecurity()) will override the SecurityContextRepository used by the springSecurityFilterChain to first try the TestSecurityContextHolder.

Migrating a Waffle Spring Security XML configuration to Spring Boot

I'm trying to use Waffle authentication with Spring Security, in a Spring Boot fashion. Expected result is 'block everything if Negotiate fails'.
Waffle project provides a configuration example for this kind of use case (there is in this example a fallback to simple HTTP auth if Negotiate fails, which I don't need), assuming configuration is done through web.xml. But despite many attempts, I don't understand how to plug Waffle with Spring Security using Boot and Java-only configuration. I'm using Spring Boot 1.2.1.RELEASE with starters web and security, Waffle version is 1.7.3.
I realize that this is not a specific question but Spring forum now redirects here and Waffle guys don't know about Spring Boot. Could someone help me translate an XML Spring Security configuration to Spring Boot?
First step is declaring a filter chain and context loader listener.
<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>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/waffle-filter.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
I'm assuming (am I wrong?) that this is already handled by #EnableWebMvcSecurity, so nothing to do here.
Next is declaring a couple of provider beans, so I translate this
<bean id="waffleWindowsAuthProvider" class="waffle.windows.auth.impl.WindowsAuthProviderImpl" />
<bean id="negotiateSecurityFilterProvider" class="waffle.servlet.spi.NegotiateSecurityFilterProvider">
<constructor-arg ref="waffleWindowsAuthProvider" />
</bean>
<bean id="basicSecurityFilterProvider" class="waffle.servlet.spi.BasicSecurityFilterProvider">
<constructor-arg ref="waffleWindowsAuthProvider" />
</bean>
<bean id="waffleSecurityFilterProviderCollection" class="waffle.servlet.spi.SecurityFilterProviderCollection">
<constructor-arg>
<list>
<ref bean="negotiateSecurityFilterProvider" />
<ref bean="basicSecurityFilterProvider" />
</list>
</constructor-arg>
</bean>
<bean id="waffleNegotiateSecurityFilter" class="waffle.spring.NegotiateSecurityFilter">
<property name="Provider" ref="waffleSecurityFilterProviderCollection" />
</bean>
to this
#Bean
public WindowsAuthProviderImpl waffleWindowsAuthProvider() {
return new WindowsAuthProviderImpl();
}
#Bean
#Autowired
public NegotiateSecurityFilterProvider negotiateSecurityFilterProvider(final WindowsAuthProviderImpl windowsAuthProvider) {
return new NegotiateSecurityFilterProvider(windowsAuthProvider);
}
#Bean
#Autowired
public BasicSecurityFilterProvider basicSecurityFilterProvider(final WindowsAuthProviderImpl windowsAuthProvider) {
return new BasicSecurityFilterProvider(windowsAuthProvider);
}
#Bean
#Autowired
public SecurityFilterProviderCollection waffleSecurityFilterProviderCollection(final NegotiateSecurityFilterProvider negotiateSecurityFilterProvider, final BasicSecurityFilterProvider basicSecurityFilterProvider) {
final SecurityFilterProvider[] securityFilterProviders = {
negotiateSecurityFilterProvider,
basicSecurityFilterProvider
};
return new SecurityFilterProviderCollection(securityFilterProviders);
}
#Bean
#Autowired
public NegotiateSecurityFilter waffleNegotiateSecurityFilter(final SecurityFilterProviderCollection securityFilterProviderCollection) {
final NegotiateSecurityFilter negotiateSecurityFilter = new NegotiateSecurityFilter();
negotiateSecurityFilter.setProvider(securityFilterProviderCollection);
return negotiateSecurityFilter;
}
Final step is sec:http section configuration. An entry point is declared and filter is placed before BASIC auth filter.
Example:
<sec:http entry-point-ref="negotiateSecurityFilterEntryPoint">
<sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
<sec:custom-filter ref="waffleNegotiateSecurityFilter" position="BASIC_AUTH_FILTER" />
</sec:http>
<bean id="negotiateSecurityFilterEntryPoint" class="waffle.spring.NegotiateSecurityFilterEntryPoint">
<property name="Provider" ref="waffleSecurityFilterProviderCollection" />
</bean>
My Boot translation:
#Autowired
private NegotiateSecurityFilterEntryPoint authenticationEntryPoint;
#Autowired
private NegotiateSecurityFilter negotiateSecurityFilter;
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests().anyRequest().authenticated()
.and()
.addFilterBefore(this.negotiateSecurityFilter, BasicAuthenticationFilter.class)
.httpBasic().authenticationEntryPoint(this.authenticationEntryPoint);
}
#Bean
#Autowired
public NegotiateSecurityFilterEntryPoint negotiateSecurityFilterEntryPoint(final SecurityFilterProviderCollection securityFilterProviderCollection) {
final NegotiateSecurityFilterEntryPoint negotiateSecurityFilterEntryPoint = new NegotiateSecurityFilterEntryPoint();
negotiateSecurityFilterEntryPoint.setProvider(securityFilterProviderCollection);
return negotiateSecurityFilterEntryPoint;
}
Running this configuration leads to strange behavior: sometimes NTLM is triggered and succeed, sometimes Negotiate filter crashes with an 'invalid token supplied' error (same credentials, user, browser, configuration).
Provided example works like a charm, which makes me think that my Boot configuration is in question.
Any help appreciated!
Spring Boot auto-registers all Filter beans so in this case the NegotiateSecurityFilter ends up being twice in the filter chain.
You have to disable the auto-registration for this specific Filter by creating a FilterRegistrationBean overriding this behavior:
#Bean
public FilterRegistrationBean registration(NegotiateSecurityFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
Also, as Dave Syer mentioned, you should be setting the authentication entry point bean using the ExceptionHandlingConfigurer.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint);
// ...
}

Trouble configuring FORM authentication for Spring Boot/Security App using Pre-Authentication

I have successfully configured a spring boot application to use spring security pre-authentication framework, relying on Tomcat 7 container to authenticate from the tomcat-users.xml file. I was also able to add a custom AuthenticationUserDetailsService to map authorities to users authenticated by the container (I don't want to manage application roles in the container). It took me awhile to get the Java configuration down but I got it all working like a charm....but ONLY when I use BASIC authentication (as configured in web.xml). When I try to convert to FORM authentication, things go wrong in the middle of the filter chain.
The login form, as configured in web.xml, is rendering (using themeleaf as in this example). I also see from the logs that the user is successfully authenticating which means that is communicating with the container as expected. I also see the J2eePreAuthenticatedProcessingFilter picking up the J2EE principal and calling the custom AuthenticationUserDetailsService to map the roles. However, a little bit further down in the logs it appears that the HttpSessionSecurityContextRepository gets a null object for SPRING_SECURITY_CONTEXT (while going through the filter chain for '/login'?). As a result, seems to have no reference to the J2EE principal anymore and subsequently creates a SecurityContextHolder with an 'anonymous token'. It does this despite the fact that I see it find the '[permitAll]' that I have configured for the '/login' url (in Spring config).
Here is an excerpt from my web.xml:
<!-- Define a Security Constraint on this Application -->
<security-constraint>
<web-resource-collection>
<web-resource-name>Secured (entire app)</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>USER</role-name>
</auth-constraint>
</security-constraint>
<!-- Define the Login Configuration for this Application -->
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login</form-login-page>
<form-error-page>/login?error</form-error-page>
</form-login-config>
</login-config>
<!-- Security roles referenced by this web application -->
<security-role>
<description>
The role that is required to access the application
</description>
<role-name>USER</role-name>
</security-role>
Accordingly, my tomcat-users.xml has a user with username of 'user' and a role of 'USER'. I don't precede with 'ROLE_' as I am not trying to map any roles into the app. I use my custom AuthenticationUserDetailsService for this.
Here is my main config file. Note, I also have a another class that implements the SpringBootServletInitializer and configures the SpringApplicationBuilder to point to this class:
#EnableAutoConfiguration
#ComponentScan
#EnableGlobalMethodSecurity(securedEnabled = true)
public class MyApplicationConfig extends WebMvcConfigurerAdapter {
#Controller
protected static class HomeController {
// ROLE_MYAPP_USER is mapped to all authenticated users by custom AuthenticationUserDetailsService
#RequestMapping({"/", "/about", "/profile"})
#Secured("ROLE_MYAPP_USER")
public ModelAndView index(#CurrentUser User user) {
Map<String, Object> model = new HashMap<>();
model.put("currentUser", user);
//noinspection SpringMVCViewInspection
return new ModelAndView("index", model);
}
}
public static void main(String[] args) throws Exception {
new SpringApplicationBuilder(VodConsoleApplication.class).run(args);
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new CurrentUserHandlerMethodArgumentResolver());
}
#Bean
public ApplicationSecurity applicationSecurity() {
return new ApplicationSecurity();
}
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable();
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/error").permitAll()
.antMatchers("/css/**").permitAll()
.antMatchers("/img/**").permitAll()
.antMatchers("/js/**").permitAll()
.antMatchers("/partials/**").permitAll()
.antMatchers("/vendor/**").permitAll()
.anyRequest().authenticated()
.and()
.jee()
.authenticatedUserDetailsService(new InMemoryUserDetailsService());
http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/");
}
}
}
There is also a login.html form that posts to /j_security_check. Let me know if you need that although it is pretty straight forward and it appears (from the logs) that it is working.
Again, everything works PERFECTLY with BASIC authentication configured in web.xml. Once I go to FORM, I get the dropping of the J2EE principal in the middle of the filter processing chain.
Ideas? Could it be that I need to configure my web.xml security constraints differently?
Any help would be GREATLY appreciated.
Solved. It turns out that I also needed to add url patterns to all my 'public' resources into the web.xml. Specifically, the javascript (in 'vendor' and 'js' directories), the css, img, etc. Here is my updated web.xml:
<security-constraint>
<web-resource-collection>
<web-resource-name>Public resources</web-resource-name>
<url-pattern>/vendor/*</url-pattern>
<url-pattern>/js/*</url-pattern>
<url-pattern>/css/*</url-pattern>
<url-pattern>/img/*</url-pattern>
<url-pattern>/partials/*</url-pattern>
</web-resource-collection>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>Secured (entire app)</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>USER</role-name>
</auth-constraint>
</security-constraint>
<!-- Define the Login Configuration for this Application -->
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login</form-login-page>
<form-error-page>/login?error</form-error-page>
</form-login-config>
</login-config>
<!-- Security roles referenced by this web application -->
<security-role>
<description>
The role that is required to access the application
</description>
<role-name>USER</role-name>
</security-role>

How to use/configure JAX-RS 2.0, SpringSecurity 3.1.+, EJB 3.2 all together

I am currently trying to setup a project with these main technologies:
Java EE 7
EJB 3.2
JAX-RS (Jersey) 2.0
Glassfish 4
Spring Security 3.1.5
I saw that it is possible to write something like that
#Stateless
#Path("apath")
public class WebResource {
#EJB
private SomeService serviceInjected;
#GET
public Response doSomething() {
return Response.ok(injectedService.doSomethingElse()).build();
}
}
Then, this means that the SomeService Session Bean is injected by the container and once we call the path: :///apath, everything is working fine.
Now, what I try to achieve is to integrate the SpringSecurity framework in that code. So my code become this:
#Component
#Stateless
#Path("apath")
public class WebResource {
#EJB
private SomeService serviceInjected;
#GET
#PreAuthorized("hasPermission('ROLE_SOMETHING')")
public Response doSomething() {
return Response.ok(injectedService.doSomethingElse()).build();
}
}
But, this does not work. Everything excepted the SpringSecurity annotations continue to work. The authorization annotations are just not taken into account.
In SpringSecurity configuration file, I have something like that:
<security:global-method-security
access-decision-manager-ref="preVoteAccessDecisionManager"
pre-post-annotations="enabled" />
with everything related to the filter chain and so correctly configured. For example, I have that:
<beans:bean id="securityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<beans:property name="securityMetadataSource">
<security:filter-security-metadata-source>
<security:intercept-url pattern="/**" access="ROLE_TEST" />
</security:filter-security-metadata-source>
</beans:property>
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="accessDecisionManager" ref="accessDecisionManager" />
</beans:bean>
And I see in my Glassfish 4 server logs that SpringSecurity managed the ROLE_TEST access for my authenticated user. I also see that my user authenticated has the list of roles that I expect.
I also tried to use this configuration and rely on javax.annotation.security annotations as below:
<security:global-method-security
access-decision-manager-ref="preVoteAccessDecisionManager"
jsr250-annotations="enabled" />
#Stateless
#Path("apath")
public class WebResource {
#EJB
private SomeService serviceInjected;
#GET
#RolesAllowed("ROLE_SOMETHING")
public Response doSomething() {
return Response.ok(injectedService.doSomethingElse()).build();
}
}
This time, the annotation is working and an exception is thrown when the user is authenticated. But in this case, my user has the roles but the SecurityContext used by the container is not filled with the Principal and roles information related to the user authenticated by SpringSecurity.
Finally, my question(s). Is there a way to integrate the JAX-RS / #Stateless / SpringSecurity Authorization together? If not, is there a way to fill a SecurityContext from SrpingSecurity to allow javax.annotation.security to work like a charm?
Thanks in advance for any helps, tips, tricks or anything else that can solve my problems :D
Spring Security's method security annotations will normally only work with Spring beans whose lifecycle is controlled by Spring. This doesn't include EJBs. However, if you wish you can use the AspectJ integration which will work for any object including EJB instances. There's a sample application in the Spring Security codebase which you can use as a reference. It might also be worth considering whether you need to use EJBs at all.

How can I access the HttpServletRequest object during user authentication in Spring Security?

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.

Resources