I have simple crud with spring-mcv and spring-security.
Everything its up and running
spring-security.xml
<b:bean id="handlerWeb1" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler">
<b:property name="permissionEvaluator" ref="permissionEvaluator"/>
</b:bean>
<b:bean id="handlerMethod2" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<b:property name="permissionEvaluator" ref="permissionEvaluator"/>
</b:bean>
<global-method-security pre-post-annotations="enabled">
<expression-handler ref="handlerMethod2"/>
</global-method-security>
<http auto-config="true" use-expressions="true" >
<expression-handler ref="handlerWeb1" />
...
</http>
The logger
INFO: Using bean 'handlerWeb1' as web SecurityExpressionHandler implementation
INFO: Using bean 'handlerMethod2' as method ExpressionHandler implementation
When the fallow methodo is executed:
#RequestMapping("/page")
#PreAuthorize("hasPermission('page','list')")
public ModelAndView pages() {
return modelAndView( ... ));
}
If hasPermission('page','list')==true there is no problem.
If hasPermission('page','list')==false the methodo still render the view but the vatiables passed tto de view are empty.
Ask:
When hasPermission('page','list')==false, what should happen?
a) redirect to 403
b) render the view with variable empty.
Related
I am trying to configure a Spring MVC Integration test using a combination of XML config and #Configuration annotated classes.
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#TestPropertySource({"/spring-security.properties",
"/rabbitmq-default.properties",
"/mongodb-default.properties",
"/webapp-override.properties"})
#ContextHierarchy({
#ContextConfiguration("classpath:**/security-config.xml"),
#ContextConfiguration(classes = RootConfig.class),
#ContextConfiguration(classes = SpringMvcConfig.class)
})
public class BaseConfiguredMvcIntegrationTest {
}
The java configurations are initialized correctly. The problem is although the "**/security-config.xml" file is found and parsed during initialization... all the spring security beans defined in there are never registered in the WebApplicationContext.
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1301)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1047)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:942)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:533)
So my question is how do you utilize both XML based and annotated based configuration in a Spring MVC Integration test?
I could change the spring security config to java/annotated based one... I would rather not do this. I find using the spring security namespace more readable and concise than using the java config.
Also, note this combined XML/Java configuration works perfectly fine in a non-test environment.
Spring v4.1.6
Spring Security v4.0.1
WebApplicationContext Config:
package com.gggdw.web.config;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.DispatcherServlet;
#Configuration
public class GGGWebInitializer implements WebApplicationInitializer {
public static final String SERVLET_NAME = "ggg";
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(RootConfig.class);
// Manage the lifecycle of the root application context
servletContext.addListener(new ContextLoaderListener(rootContext));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
dispatcherContext.register(SpringMvcConfig.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher = servletContext.addServlet(SERVLET_NAME, new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
//Spring security config
FilterRegistration.Dynamic springSecurityFilterChain = servletContext.addFilter(
"securityFilter", new DelegatingFilterProxy("springSecurityFilterChain"));
springSecurityFilterChain.addMappingForServletNames(null, false, SERVLET_NAME);
//springSecurityFilterChain.setAsyncSupported(true);
servletContext.addFilter("hiddenHttpMethodFilter", HiddenHttpMethodFilter.class);
}
}
RootConfig.class
#Configuration
#Import({WebPropertiesConfig.class, // loads all properties files on class path from resources folder
MongoConfig.class // init mongodb connection
})
#ImportResource({"classpath:**/security-config.xml"}) // spring security xml config (java config not as readable)
public class RootConfig {
}
security-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- <context:property-placeholder location="classpath:spring-security.properties" /> -->
<security:global-method-security pre-post-annotations="enabled" secured-annotations="enabled">
<security:expression-handler ref="expressionHandler"/>
</security:global-method-security>
<bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<property name="permissionEvaluator" ref="permissionEvaluator"/>
</bean>
<bean id="permissionEvaluator"
class="com.my.local.package.security.GenericPermissionEvaluator">
</bean>
<!-- Configure Spring Security -->
<security:http auto-config="true" use-expressions="true" >
<security:form-login login-page="${spring.security.login-page}"
login-processing-url="${spring.security.login-processing-url}"
default-target-url="${spring.security.default-target-url}"
authentication-failure-url="${spring.security.authentication-failure-url}"
username-parameter="${spring.security.username-parameter}"
password-parameter="${spring.security.password-parameter}"
/>
<security:logout logout-url="${spring.security.logout-url}"
logout-success-url="${spring.security.logout-success-url}" />
<security:intercept-url pattern="/**" requires-channel="https" />
<security:intercept-url pattern="/s/**" access="isAuthenticated()" requires-channel="https" />
<security:custom-filter ref="log4JMDCFilter" after="SECURITY_CONTEXT_FILTER"/>
<security:access-denied-handler error-page="${spring.security.access-denied-handler-error-page}" />
<!-- <security:session-management invalid-session-url="${spring.security.invalid-session-url}"/>
2 types of invalid session, brand new user and a timeout of a previous logged in user
both need to be handled differently -->
</security:http>
<bean id="customUserDetailsService" class="com.my.local.package.CustomUserDetailsService" depends-on="userRepository"/>
<bean id="bCryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
<!-- log4j filter to add userName and ipAddress into logging on a per request/thread basis -->
<bean id="log4JMDCFilter" class="com.my.local.package.filter.Log4JMDCFilter"/>
<security:authentication-manager>
<security:authentication-provider user-service-ref="customUserDetailsService">
<security:password-encoder ref="bCryptPasswordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
</beans>
UPDATE: upon further consideration and based on your latest feedback, the behavior you're experiencing might be the result of a bug that was introduced in Spring Framework 4.1.4 (see SPR-13075 for details).
Try downgrading to Spring Framework 4.1.3, and let me know if you still experience the undesired behavior.
note this combined XML/Java configuration works perfectly fine in a non-test environment.
How so?
Do you literally have three (3) contexts loaded in a hierarchy in production?
I doubt that. Rather, I assume you are somehow loading a single root WebApplicationContext from "classpath:**/security-config.xml" and RootConfig.class.
Thus, the most important question is: How are you configuring the root WebApplicationContext in production?
Once you have answered that, I can tell you how to achieve the same thing in your test configuration. ;)
Regards,
Sam (author of the Spring TestContext Framework)
Pay attention to the note from PathMatchingResourcePatternResolver:
Note that "classpath*:" when combined with Ant-style patterns will only work reliably with at least one root directory before the pattern starts, unless the actual target files reside in the file system. This means that a pattern like "classpath*:*.xml" will not retrieve files from the root of jar files but rather only from the root of expanded directories. This originates from a limitation in the JDK's ClassLoader.getResources() method which only returns file system locations for a passed-in empty String (indicating potential roots to search).
I am discovering oauth2. I have managed to create a sample which return JWToken and REST protected by this token.
Now i would like to improve this by adding access-control in my protected REST interface.
Why ? Because i would like users like ADMIN, READER access some URL or not.
Following http://projects.spring.io/spring-security-oauth/docs/oauth2.html
it is possible over expression-handler in http node.
Here is the configuration i have added to my xml config :
<sec:global-method-security
pre-post-annotations="enabled" />
<sec:http pattern="/protected/**" create-session="never" entry-point-ref="oauthAuthenticationEntryPoint">
<sec:anonymous enabled="false" />
<sec:intercept-url pattern="/protected/**" />
<sec:custom-filter ref="resourceServerFilter"
before="PRE_AUTH_FILTER" />
<sec:access-denied-handler ref="oauthAccessDeniedHandler" />
<sec:expression-handler ref="myexpressionHandler" />
</sec:http>
<bean id="myexpressionHandler" class="org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler">
</bean>
log :
févr. 04, 2015 4:09:31 PM org.springframework.security.config.method.GlobalMethodSecurityBeanDefinitionParser parse
INFOS: Expressions were enabled for method security but no SecurityExpressionHandler was configured. All hasPermision() expressions will evaluate to false.
févr. 04, 2015 4:09:31 PM org.springframework.security.config.http.HttpSecurityBeanDefinitionParser checkFilterChainOrder
But with my JWTtoken I successfully get the protected resource.
My controller:
#Component
#RestController
#RequestMapping(value = "/protected")
public class HelloWorldRest {
private static final Logger LOG = LoggerFactory
.getLogger(HelloWorldRest.class);
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#PreAuthorize("#oauth2.clientHasRole('ROLE_ADMIN')")
#RequestMapping(value = "/greeting/{name}")
public Greeting greeting(#PathVariable String name) {
LOG.info("Fonction greeting : " + name);
return new Greeting(counter.incrementAndGet(), String.format(template,
name + ", I am Mister Toto"));
}
}
I have tested with an user who get authoritiesGrant={ ROLE_NONE }
Thanks,
Any ideas ?
To avoid the error message "SecurityExpressionHandler was configured" you should add the expression handler to your global-method-security. Like this:
<sec:global-method-security pre-post-annotations="enabled">
<sec:expression-handler ref="oauthExpressionHandler" />
</sec:global-method-security>
<oauth:expression-handler id="oauthExpressionHandler" />
Instead of defining your own bean for the WebSecurityExpressionHandler (as you dod in the question) you can additionally use:
<oauth:web-expression-handler id="oauthWebExpressionHandler" />
Hi all I am going to integrate authentication for my restful web services in my web application. I already integrate Spring security 3.1 for my entire application and its working good; but I am confused and I really stuck, how to integrate security for web services in existing application?
Here my existing security configuration for authentication and authorization.
securityApplicationContext.xml :
<beans:bean id="myAccessDecisionManager"
class="com.security.repository.MyAccessDecisionManager">
<beans:property name="customAuthenticatiuonService" ref="customAuthenticatiuonServiceImpl"> </beans:property>
</beans:bean>
<http auto-config="true" once-per-request="true"
access-decision-manager-ref="myAccessDecisionManager" access-denied-page="/jsp/errorPage.jsp">
<intercept-url pattern="/*.web" access="ROLE_ANONYMOUS" />
<intercept-url pattern="productsService/*.web" access="ROLE_ADMIN" />
<!-- Override default login and logout pages -->
<form-login login-page="/login.works" login-processing-url="/j_spring_security_check"
default-target-url="/login/validate.works"
authentication-failure-url="/login.works?login_error=1" />
<logout logout-url="/j_spring_security_logout"
logout-success-url="/login.works" invalidate-session="true" />
<session-management invalid-session-url="/login.works"
session-fixation-protection="newSession">
<concurrency-control max-sessions="100"
error-if-maximum-exceeded="false" />
</session-management>
</http>
<authentication-manager>
<authentication-provider ref="customAuthenticationProvider"></authentication-provider>
</authentication-manager>
<beans:bean id="customAuthenticationProvider"
class="com.security.repository.CustomAuthenticationProvider">
<beans:property name="customAuthenticatiuonService" ref="customAuthenticatiuonServiceImpl"> </beans:property>
</beans:bean>
<beans:bean id="customAuthenticatiuonServiceImpl"
class="com.service.impl.CustomAuthenticatiuonServiceImpl">
<beans:property name="customAuthenticationDAO" ref="customAuthenticationDAOTarget"> </beans:property>
</beans:bean>
<beans:bean id="customAuthenticationDAOTarget" class="com.dao.impl.CustomAuthenticatiuonDAOImpl">
<beans:property name="hibernateTemplate" ref="cessHibernateTemplate"/>
</beans:bean>
now I am looking to secure my web services which as follows:
My Web Service:
#Component
#Path("/productsService")
//#RequestMapping("/productsService")
#Scope("request")
#Controller
public class ProductsController {
#Autowired
private ProductsService products;
#GET
#Path("/getProductsList.lbt")
// #RequestMapping("/getProductsList.lbt")
#Produces("text/plain")
public String getProductsList() {
return products.getProductsList();
}
}
#Service("products")
public class ProductsService {
#Secured("ROLE_ADMIN")
public String getProductsList() {
return "Test String for Rest Web Service";
}
}
and last my Client class:
public static void main(String[] args) {
Client c = Client.create();
// plain text
WebResource r = c
.resource("http://localhost:8080/productsService/getProductsList.web");
c.addFilter(new HTTPBasicAuthFilter("admin", "admin"));
c.setFollowRedirects(false);
System.out.println("Plain Text=>> " + r.get(String.class));
}
I am using customAuthenticationManager and myAccessDecisionManger for authentication and authorization of user.
When I use #Path annotation on controller and debug it then debugger could not go to my controller and throws an error for 402 not found and when I use #RequestMapping then it goes properly but when returning from controller I get the error at client side 302 found.
How to resolve this? Please help me.
Thanks in advance.
You should use two http tags. One for your web application and the other one for your REST API. Let's say, you can use an entry point web/** for your web app and an entry point api/** for your REST API. You propaply want to secure your API with HTTP Basic, so your web app should work with form login (that uses java session) and your REST API with HTTP Basic authentication.
REST APIs are better secured with OAuth 2, but depending on size or audience of your application would be overkill.
I am using Spring Security version 3.1.2.
Here is the configuration:
<http pattern="/embedded/**" auto-config="true" use-expressions="true" access-denied-page="/embedded/login.htm">
<intercept-url pattern="/embedded/login-embedded.html" access="hasRole('ROLE_AUTHENTICATED')"/>
<intercept-url pattern="/embedded/**" access="permitAll"/>
<form-login login-page="/embedded/login.htm"
authentication-failure-url="/embedded/login.htm?error=true"
default-target-url="/embedded/login-embedded.html" />
<logout logout-success-url="/embedded/index.html"/>
</http>
<http auto-config="true" use-expressions="true" access-denied-page="/login.htm">
<intercept-url pattern="/login-success.html" access="hasRole('ROLE_AUTHENTICATED')"/>
<intercept-url pattern="/**" access="permitAll"/>
<form-login login-page="/login.htm"
authentication-failure-url="/login.htm?error=true"
default-target-url="/login-success.html"/>
<logout logout-success-url="/index.html"/>
</http>
I POST data to a Spring MVC controller which calls a service to validate a captcha. If that passes it forwards it to the j_spring_security_check RequestDispatcher.
Here is the relevant part of the controller:
#RequestMapping(value ="/embedded/login.htm", method = RequestMethod.POST)
public String authenticateCaptcha(HttpServletRequest request,
HttpServletResponse response,
#RequestParam String verificationText) throws IOException, ServletException {
HttpSession session = request.getSession();
String sessionId = session.getId();
if (captchaService.validate(sessionId, verificationText)) {
request.getRequestDispatcher("/j_spring_security_check").forward(request, response);
return null;
}
return buildErrorRedirect(request);
}
My problem is that after captcha is validated and the request is forwarded to Spring Security and authentication fails there the error page it forwards to is /login.htm?error=true instead of /embedded/login.htm?error=true.
URL /j_spring_security_check doesn't match /embedded/** so authentication-failure-url="/login.htm?error=true" is used - the one from second configuration.
Similar question has been asked recently:
Spring security with two realms, first default-target-url is never invoked
And one of the creators of Spring Security answered it. I recommend reading it.
Another worthy piece of Stack Overflow:
Why does a forwarded request pass through filter chain again?
First of all, I don't know how can config restful url request for spring webflow,
for example,how can I invoke my webflow when type address:
http://localhost/app/order/edit/1002
It's easy to write spring mvc controller to handle this,but in case of webflow, I don't know how to pass parameters.
Can anybody help me?
Thanks
Try reading request parameter like following.
It processes "http://example.com/message?messageId=3" but when rendering view, the URL changes to something like "http://example.com/message?execution=e1s1".
Flow code:
<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<on-start>
<evaluate expression="FooWebFlowController.create(flowRequestContext)"
result="flowScope.fooModel"/>
</on-start>
<view-state id="view" model="fooModel" view="fooView">
</view-state>
</flow>
FooWebFlowController bean:
import org.springframework.webflow.execution.RequestContext;
#Component
public class FooWebFlowController {
#Autowired
private FooDAO fooDAO;
public Foo create(RequestContext requestContext) {
String messageId = requestContext.getRequestParameters().get("messageId")
Foo foo = fooDAO.findByMessagId(messageId);
return foo;
}
}
Is the RequestPathFlowExecutorArgumentHandler what you're looking for?
Flow executor argument handler that
extracts arguments from the request
path and exposes them in the URL path.
This allows for REST-style URLs to
launch flows in the general format:
http://${host}/${context
path}/${dispatcher path}/${flowId}
<bean id="flowController" class="org.springframework.webflow.executor.mvc.FlowController">
<property name="flowExecutor" ref="flowExecutor" />
<property name="argumentHandler">
<bean class="org.springframework.webflow.executor.support.RequestPathFlowExecutorArgumentHandler" />
</property>
</bean>