I tried to configure spring to allow encoded slashes in URL, but I cannot get the configuration to be applied
I've tried adding #EnableWebSecurity but it didn't seem to change anything. When I put breakpoints, the #Bean method is called but not configure().
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
DefaultHttpFirewall firewall = new DefaultHttpFirewall();
firewall.setAllowUrlEncodedSlash(true);
return firewall;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
}
}
I managed to make it work using xml configuration. I added the following in my applicationContext.xml:
<bean id="allowUrlEncodedSlashHttpFirewall"
class="org.springframework.security.web.firewall.DefaultHttpFirewall">
<property name="allowUrlEncodedSlash" value="true"/>
</bean>
<security:http-firewall ref="allowUrlEncodedSlashHttpFirewall"/>
Related
I'm trying to remove the "ROLE_" prefix in Spring Security. The first thing I tried was:
http.servletApi().rolePrefix("");
That didn't work, so I tried creating a BeanPostProcessor as suggested in http://docs.spring.io/spring-security/site/migrate/current/3-to-4/html5/migrate-3-to-4-jc.html#m3to4-role-prefixing-disable. That didn't work either.
Finally, I tried creating my own SecurityExpressionHandler:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.expressionHandler(webExpressionHandler())
.antMatchers("/restricted").fullyAuthenticated()
.antMatchers("/foo").hasRole("mycustomrolename")
.antMatchers("/**").permitAll();
}
private SecurityExpressionHandler<FilterInvocation> webExpressionHandler() {
DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
defaultWebSecurityExpressionHandler.setDefaultRolePrefix("");
return defaultWebSecurityExpressionHandler;
}
However, this doesn't work either. If I use "hasAuthority(roleName)" instead of hasRole, it works as expected.
Is it possible to remove the ROLE_ prefix from Spring Security's hasRole check?
Starting from Spring 4.2, you can define the prefix with a single bean, as described here: https://github.com/spring-projects/spring-security/issues/4134
#Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults(""); // Remove the ROLE_ prefix
}
XML version:
<beans:bean id="grantedAuthorityDefaults" class="org.springframework.security.config.core.GrantedAuthorityDefaults">
<beans:constructor-arg value="" />
</beans:bean>
The following configuration works for me.
#Override
public void configure(WebSecurity web) throws Exception {
web.expressionHandler(new DefaultWebSecurityExpressionHandler() {
#Override
protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, FilterInvocation fi) {
WebSecurityExpressionRoot root = (WebSecurityExpressionRoot) super.createSecurityExpressionRoot(authentication, fi);
root.setDefaultRolePrefix(""); //remove the prefix ROLE_
return root;
}
});
}
It appears the new GrantedAuthorityDefaults will change the prefix for the DefaultWebSecurityExpressionHandler and the DefaultMethodSecurityExpressionHandler, but it doesn't modify the RoleVoter.rolePrefix that is setup from #EnableGlobalMethodSecurity.
The RoleVoter.rolePrefix is what is used for #Secured("ADMIN") style of method security.
So along with the GrantedAuthorityDefaults, I had to also add this CustomGlobalMethodSecurity class to override the defaults for RoleVoter.
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true)
public class CustomGlobalMethodSecurity extends GlobalMethodSecurityConfiguration {
protected AccessDecisionManager accessDecisionManager() {
AffirmativeBased accessDecisionManager = (AffirmativeBased) super.accessDecisionManager();
//Remove the ROLE_ prefix from RoleVoter for #Secured and hasRole checks on methods
accessDecisionManager.getDecisionVoters().stream()
.filter(RoleVoter.class::isInstance)
.map(RoleVoter.class::cast)
.forEach(it -> it.setRolePrefix(""));
return accessDecisionManager;
}
}
If you use Spring Boot 2, you can create this bean to override the RoteVoter prefix
#Bean
public GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("<anything you want>");
}
It works because when GlobalMethodSecurityConfiguration creates AccessDecisionManager in the method GlobalMethodSecurityConfiguration.accessDecisionManager(). Here is the snippet of code, notice the null check on grantedAuthorityDefaults
protected AccessDecisionManager accessDecisionManager() {
....
RoleVoter roleVoter = new RoleVoter();
GrantedAuthorityDefaults grantedAuthorityDefaults =
getSingleBeanOrNull(GrantedAuthorityDefaults.class);
if (grantedAuthorityDefaults != null) {
roleVoter.setRolePrefix(grantedAuthorityDefaults.getRolePrefix());
}
decisionVoters.add(roleVoter);
decisionVoters.add(new AuthenticatedVoter());
return new AffirmativeBased(decisionVoters);
}
If you are prior to 4.2 and are using so called voters (you are if you use annotations like #hasRole etc) then you need to define below beans in the context:
#Bean
public DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler() {
DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler();
defaultMethodSecurityExpressionHandler.setDefaultRolePrefix("");
return defaultMethodSecurityExpressionHandler;
}
#Bean
public DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler() {
DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
defaultWebSecurityExpressionHandler.setDefaultRolePrefix("");
return defaultWebSecurityExpressionHandler;
}
These beans are used to create evaluation context for spell expressions and they have a defaultRolePrefix set to 'ROLE_'. Although it depends on your use case. This one worked for me and above didn't.
EDIT: answering question about xml configuration -> of course it can be done in xml. Everything done in java config can be written in xml configuration. Here is example (although I did not test it so there might be a typo or something):
<bean id="defaultWebSecurityExpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler">
<property name="defaultRolePrefix" value=""></property>
</bean>
<bean id="defaultMethodSecurityExpressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<property name="defaultRolePrefix" value=""></property>
</bean>
I post summary working solutions for me:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
/**
* Allow skip ROLE_ when check permission using #Secured, like:
* #Secured({AuthorityConstants.ROLE_SYSTEM_ADMIN})
*/
#Override
protected AccessDecisionManager accessDecisionManager() {
AffirmativeBased accessDecisionManager = (AffirmativeBased) super.accessDecisionManager();
setAuthorityRolePrefix(accessDecisionManager, "");
return accessDecisionManager;
}
private void setAuthorityRolePrefix(AffirmativeBased accessDecisionManager, String rolePrefix) {
accessDecisionManager.getDecisionVoters().stream()
.filter(RoleVoter.class::isInstance)
.map(RoleVoter.class::cast)
.forEach(it -> it.setRolePrefix(rolePrefix));
}
/**
* Allow skip ROLE_ when check permission using #PreAuthorize, like:
* #PreAuthorize("hasAnyRole('USER', 'SYSTEM_ADMIN')")
*/
#Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults(""); // Remove the ROLE_ prefix
}
}
With Spring Boot 2.3 I got this exception at boot time:
Error creating bean with name 'resourceHandlerMapping' defined in class path resource
[org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]:
Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [org.springframework.web.servlet.HandlerMapping]:
Factory method 'resourceHandlerMapping' threw exception;
nested exception is java.lang.IllegalStateException: No ServletContext set
Here is my solution:
#Configuration
#Import(RolePrefixConfiguration.class)
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
public static class RolePrefixConfiguration {
#Bean
public GrantedAuthorityDefaults grantedAuthorityDefaults() {
log.debug("remove prefix 'ROLE_' from grantedAuthorityDefaults");
return new GrantedAuthorityDefaults("");
}
}
// ... your usual config
}
This worked for me.
public SimpleAuthorityMapper grantedAuthority()
{
SimpleAuthorityMapper mapper = new SimpleAuthorityMapper();
mapper.setPrefix("");
return mapper;
}
#Override
public void configure(AuthenticationManagerBuilder auth)
{
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(grantedAuthority());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
I have the following situation:
#Controller
public class myController {
#Autowired
private IProxy service;
public ModelAndView init(HttpServletRequest request, HttpServletResponse response) throws Exception {
List<String> list = service.getName();
}
}
Then my Service is define as follow:
public interface IProxy {
public List<String> getName();
}
Proxy class is responsible for the lookup to the remote bean
#Service("service")
public class Proxy implements IProxy {
...
public List<String> getName() {
return myClass.getName();
}
And the implementation is the following:
#Interceptors(interceptor.class)
#Stateless
#Resource(name = "java:/db")
#Remote(MyClassRemote.class)
public class MyClassImpl extends MyEjb implements MyClassRemote{
#PersistenceContext(unitName = "db")
private EntityManager em;
#Resource
private SessionContext sctx;
#Autowired
public IMyRepo myRepo;
#Override
public List<String> getName() {
try {
return myRepo.getName(em);
}
catch (Exception ex) {
ex.printStackTrace();
throw ex;
}
finally {}
}
So, the problem is that here myRepo is null. I don't know why because IMyRepo and his implementation are always located within the path scanned by Spring.
Just one clarification: MyRepo class that implements IMyRepo is annotated with #Repository.
Any idea?
you can inject spring beans in EJB using Spring interceptors, as explained here in the official documentation. Basically you'll need to adjust your class as follows:
// added the SpringBeanAutowiringInterceptor class
#Interceptors({ interceptor.class, SpringBeanAutowiringInterceptor.class })
#Stateless
#Resource(name = "java:/db")
#Remote(MyClassRemote.class)
public class MyClassImpl extends MyEjb implements MyClassRemote{
// your code
}
You'll also need to define the context location in a beanRefContext.xml file (with your own application context file):
application-context.xml version
<bean id="context"
class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg>
<list>
<value>application-context.xml</value>
</list>
</constructor-arg>
</bean>
Java Configuration version:
<bean id="context"
class="org.springframework.context.annotation.AnnotationConfigApplicationContext">
<constructor-arg>
<list>
<value type="java.lang.Class">com.your.app.Configuration</value>
</list>
</constructor-arg>
</bean>
Spring beans and EJB are two different things, you can't just inject a Spring bean in an EJB, because that EJB is no Spring bean, so Spring doesn't know there is a field which should be injected by Spring (unless you use some fancy AOP stuff, which can enable injection into non-Spring-managed beans).
I have a Spring boot application that needs to perform LDAP queries. I'm trying to take the following recommendation from the Spring boot documentation:
"Many Spring configuration examples have been published on the
Internet that use XML configuration. Always try to use the equivalent
Java-base configuration if possible."
In a Spring XML configuration file, I would have used:
<ldap:context-source
url="ldap://localhost:389"
base="cn=Users,dc=test,dc=local"
username="cn=testUser"
password="testPass" />
<ldap:ldap-template id="ldapTemplate" />
<bean id="personRepo" class="com.llpf.ldap.PersonRepoImpl">
<property name="ldapTemplate" ref="ldapTemplate" />
</bean>
How would I configure this using a Java-based configuration? I need to be able to change URL, base, username, and password attributes of ldap:context-source without a code rebuild.
The <ldap:context-source> XML tag produces an LdapContextSource bean and the <ldap:ldap-template> XML tag produces an LdapTemplate bean so that's what you need to do in your Java configuration:
#Configuration
#EnableAutoConfiguration
#EnableConfigurationProperties
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
#ConfigurationProperties(prefix="ldap.contextSource")
public LdapContextSource contextSource() {
LdapContextSource contextSource = new LdapContextSource();
return contextSource;
}
#Bean
public LdapTemplate ldapTemplate(ContextSource contextSource) {
return new LdapTemplate(contextSource);
}
#Bean
public PersonRepoImpl personRepo(LdapTemplate ldapTemplate) {
PersonRepoImpl personRepo = new PersonRepoImpl();
personRepo.setLdapTemplate(ldapTemplate);
return personRepo;
}
}
To allow you to change the configuration without a rebuild of your code, I've used Spring Boot's #ConfigurationProperties. This will look in your application's environment for properties prefixed with ldap.contextSource and then apply them to the LdapContextSource bean by calling the matching setter methods. To apply the configuration in the question, you can use an application.properties file with four properties:
ldap.contextSource.url=ldap://localhost:389
ldap.contextSource.base=cn=Users,dc=test,dc=local
ldap.contextSource.userDn=cn=testUser
ldap.contextSource.password=testPass
I'm trying to convert a Spring project from XML to Java config and have run into the following issue with HandlerInterceptors:
XML Config (works):
<mvc:annotation-driven />
<mvc:interceptors>
<bean class="com.mycompany.MyHandlerInterceptor" />
</mvc:interceptors>
Java Config (interceptor is never called)
#Configuration
public class MvcConfig extends WebMvcConfigurationSupport {
#Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyHandlerInterceptor());
}
// ...
}
According to the documentation, these two configurations should be equivalent, however in the Java config example the neither the pre or post handle methods are ever called?
What am I missing?
Thanks.
This was my own fault. I had overridden requestMappingHandlerMapping() in my MVC Java config and did not set the interceptors property on the custom HandlerMapping class.
#Bean
#Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
CustomRequestHandlerMapping handlerMapping = new CustomRequestHandlerMapping();
handlerMapping.setOrder(0);
handlerMapping.setInterceptors(getInterceptors()); // <-- This was missing
return handlerMapping;
}
I have log4j DailyRollingFileAppender class in which setFile() method I need to check database value to decide which file to used for logging.
DailyRollingFileAppender class
public void setFileName()
{
isLoginEnabled = authenticationManager.checkLoginLogging();
}
Here 'authenticationManager' is object of class used to make database call using spring dependency injection feature.
spring-beans.xml
<bean id="dailyRollingFileAppender" class="com.common.util.DailyRollingFileAppender">
<property name="authenticationManager">
<ref bean="authenticationManager"/>
</property>
</bean>
<bean id="authenticationManager" class="com.security.impl.AuthenticationManagerImpl">
<property name="userService">
<ref bean="userService"/>
</property>
</bean>
Now when I start my application log4j gets initiated first and since spring-beans is yet to invoked it throws NullPointerException in method setFileName().
So is there a way I can make call to 'authenticationManager.checkLoginLogging();' from DailyFileAppender class so that when log4j loads it should able to get database value?
A few years late, but I hope this is of help to someone.
I was after similar functionality - I have a custom appender, and i wanted to use an autowired bean to perform some logging using a service we'd built. By making the appender implement the ApplicationContextAware interface, and making the field that i'd normally autowire static, i'm able to inject the spring-controlled bean into the instance of the appender that log4j has instantiated.
#Component
public class SslErrorSecurityAppender extends AppenderSkeleton implements ApplicationContextAware {
private static SecurityLogger securityLogger;
#Override
protected void append(LoggingEvent event) {
securityLogger.log(new SslExceptionSecurityEvent(SecurityEventType.AUTHENTICATION_FAILED, event.getThrowableInformation().getThrowable(), "Unexpected SSL error"));
}
#Override
public boolean requiresLayout() {
return false;
}
#Override
public synchronized void close() {
this.closed = true;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
if (applicationContext.getAutowireCapableBeanFactory().getBean("securityLogger") != null) {
securityLogger = (SecurityLogger) applicationContext.getAutowireCapableBeanFactory().getBean("securityLogger");
}
}
}