I have a number of REST controllers built with Spring MVC, say for example:
#RequestMapping(value = "employee")
EmplyoeeController
#RequestMapping(value = "office")
OfficeController
#RequestMapping(value = "school")
SchoolController
#RequestMapping(value = "admin")
AdminController
#RequestMapping(value = "report")
ReportController
Only .../api/admin and .../api/report resources should be available for anyone.
I donot want to remove or comment request mappings in my other controllers. How could I achieve this in web.xml
The problem is we are releasing a lite version of our web-app, but bundled with a number of controllers. In our spring-security.xml we have:
<security:intercept-url
pattern="/api/**"
access="isAuthenticated()"
/>
If the user is authenticated then that will allow them to access all resources under /api/** - like /api/admin and also /api/office, /api/school /api/employee, and so on...
The functionality we want the authenticated users to access is only /api/admin and /api/report and nothing else even if they are authenticated.
We donot need any ROLEs to be assigned for the users yet.
You could use Spring Security for this. Tutorial here: http://spring.io/guides/gs/securing-web/.
In web.xml you would add this:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
some.package.LocalSecurityConfig
</param-value>
</context-param>
<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>
And you would define LocalSecurityConfig like this:
package some.package;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
#EnableWebSecurity
public class LocalSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> configurer = new InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder>();
configurer.withUser("user").password("password").authorities("ROLE_USER");
auth.apply(configurer);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/employee", "/office", "/school").authenticated()
.anyRequest().permitAll().and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
}
Related
I Use spring 5.0.7.RELEASE and I need to add basic authentication for my spring application and this is a pretty old application. That is why it uses the old way of registering the config classes. I have added the SecurityConfig class and imported it in the SpringMvcConfig as shown below
#Configuration
#EnableWebMvc
#ComponentScan( basePackages = {
"com.test" },
excludeFilters = {
#ComponentScan.Filter( Configuration.class )
} )
#ImportResource("classpath:/spring-context.xml")
#EnableSwagger2
#Import({SwaggerConfig.class, SecurityConfig.class})
public class SpringMvcConfig extends WebMvcConfigurationSupport {
#Autowired private ApplicationContext applicationContext;
private static final String[] RESOURCE_BUNDLES = new String[] { "MessageResources" };
private static final String[] YAML_RESOURCE_BUNDLES = new String[] { "messages" };
/**
* Configures the Jackson object mapper
*/
#Bean( name = "objectMapper" )
public ObjectMapper getObjectMapper( JacksonService jacksonService ) {
return jacksonService.getObjectMapper();
}
/**
* Configures the ApplicationContextProvider
*/
#Bean( name = "applicationContextProvider" )
public ApplicationContextProvider getApplicationContextProvider() {
return new ApplicationContextProvider();
}
}
and here is the web.xml
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.test.SpringMvcConfig</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- Spring Security Configuration -->
<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>
and here is my SecurityConfig.java
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication()
.withUser("test")
.password("testpwd")
.roles("USER");
}
}
I get a 403 forbidden The server understood the request but refuses to authorize it. I also added
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
I am integrating Spring Security into my Spring MVC and Angular App.
Versions:
Spring Security: 4.1.5.RELEASE
Spring MVC: 4.2.0.RELEASE
Angular: 4
After integration, I see that browser is showing pop up for user name and password. Even after following changes, I am not able to get rid of the pop up and proceed further to my custom login form in angular.
To start with I am using http basic authentication mechanism.
For this created following SecurityConfig.java.
[Updated]
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// #formatter:on
auth.inMemoryAuthentication().withUser("user1").password("user1").roles("USER").and().withUser("user2")
.password("user2").roles("USER").and().withUser("admin").password("admin").roles("ADMIN");
// #formatter:off
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:on
http
.authorizeRequests().antMatchers("/index.html", "/", "/login", "*.*", "/*.bundle.*", "/*.ico")
.permitAll().anyRequest().authenticated().and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
// #formatter:off
}
}
For resource and path resolution, created following ServletContextConfiguration.java .
#Configuration
#EnableWebMvc
public class ServletContextConfiguration extends WebMvcConfigurerAdapter {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(true).ignoreAcceptHeader(true).useJaf(false)
.defaultContentType(MediaType.APPLICATION_JSON).mediaType("html",
MediaType.TEXT_HTML)
.mediaType("xml", MediaType.APPLICATION_XML).mediaType("json",
MediaType.APPLICATION_JSON);
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean(name = "contentNegotiatingViewResolver")
public ViewResolver getContentNegotiatingViewResolver(ContentNegotiationManager manager) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(manager);
return resolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("/dist/").resourceChain(true)
.addResolver(new PathResourceResolver());
}
}
Following is my web.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>Archetype Created Web Application</display-name>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
<url-pattern>*.html</url-pattern>
<url-pattern>/index.html</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<!-- Spring Security -->
<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>
</web-app>
I am using weblogic server to deploy war file.
weblogic.xml
<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-web-app
xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-web-app
http://xmlns.oracle.com/weblogic/weblogic-web-app/1.4/weblogic-web-app.xsd">
<wls:context-root>/</wls:context-root>
<wls:container-descriptor>
<wls:prefer-application-packages>
<wls:package-name>org.slf4j.*</wls:package-name>
<wls:package-name>org.springframework.*</wls:package-name>
<wls:package-name>org.springframework.web.servlet.view.*</wls:package-name>
<wls:package-name>oracle.core.*</wls:package-name>
<wls:package-name>oracle.jdbc.*</wls:package-name>
<wls:package-name>oracle.net.*</wls:package-name>
<wls:package-name>oracle.sql.*</wls:package-name>
<wls:package-name>oracle.security.*</wls:package-name>
<wls:package-name>org.hibernate.*</wls:package-name>
<wls:package-name>com.fasterxml.*</wls:package-name>
</wls:prefer-application-packages>
</wls:container-descriptor>
</wls:weblogic-web-app>
Angular Changes:
In app.module.ts introduced http interceptor to inject http header X-Requested-With as suggested here.
import { BrowserModule } from '#angular/platform-browser';
import { FormsModule } from '#angular/forms';
import { HttpModule } from '#angular/http';
import { RouterModule, Routes } from '#angular/router';
import { NgModule } from '#angular/core';
import { Injectable } from '#angular/core';
import {
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HTTP_INTERCEPTORS
} from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import { AppComponent } from './app.component';
import { StoreComponent } from './components/store/store.component';
import { StoreService } from './services/store.service';
import { LoginComponent } from './components/login/login.component';
import { LogoutComponent } from './components/logout/logout.component';
#Injectable()
export class XhrInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler) {
const xhr = req.clone({
headers: req.headers.set('X-Requested-With', 'XMLHttpRequest')
});
console.log(">>>>>>>>>>HttpRequest intercepted...");
return next.handle(xhr);
}
}
const appRoutes: Routes = [
{ path:'', pathMatch: 'full', redirectTo: 'login' },
{ path: 'login', component: LoginComponent},
{ path: 'logout', component: LogoutComponent },
{ path: 'store', component: StoreComponent }
]
#NgModule({
declarations: [
AppComponent,
StoreComponent,
LoginComponent,
LogoutComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
RouterModule.forRoot(appRoutes)
],
providers: [ StoreService, { provide: HTTP_INTERCEPTORS, useClass: XhrInterceptor, multi: true } ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
By some reason, still I am able to see the default user name and password pop up from browser.
In web console, I don't see the X-Requested-With header. Also, I don't see my console log which should have been printed while http request is intercepted.
I think, request might not be reaching angular client and something wrong happened in server side.
What could be missing here?
Update:
As suggested, I have replaced http.basic() request with http.authorizeRequests(). Now, pop up is not displayed for /login request. But after once I try to submit user credentials in my custom login page by calling /authenticate, again I see the pop up :(
What could be missing here.
Please clarify.
Thanks.
this is due to http.httpBasic() in your config, it should be http.authorizeRequests()
You are still using spring security's http basic authentication, that is why is it showing basic authentication pop up, dont use it , use http.autherizeRequest() and using machers permit your login url.
And in routemodule define same path for login component. It will work.
In my web.xml I have two different dispatcher servlets, one of them has all beans from root context, second dispatcher servlet has another authenticationManager.
as sayed:
In the Web MVC framework, each DispatcherServlet
has its own WebApplicationContext, which inherits all the beans already defined in the root
WebApplicationContext. The root WebApplicationContext should contain all the infrastructure
beans that should be shared between your other contexts and Servlet instances. These inherited beans
can be overridden in the servlet-specific scope, and you can define new scope-specific beans local to
a given Servlet instance.
So my new authenticationManager must override the same bean from root context. This authenticationManager has another daoAuthenticationProvider, which has another userDetailsService. But when I want to log in in system from path for second dispathcer servlet, spring uses authenticationManager from root context.
It is web.xml:
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>ua.translate.AppConfig</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</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>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>ua.translate.AppConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>adminDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>ua.admin.AdminConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>adminDispatcher</servlet-name>
<url-pattern>/bulbular/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
It is AdminConfig.class:
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = {"ua.admin"})
#EnableTransactionManagement
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class AdminConfig extends WebMvcConfigurerAdapter{
}
It is WebSecurityConfigurerAdapter implementation, which is located in ua.admin package and have new authenticationManager:
#EnableWebSecurity
#Configuration
#ComponentScan(basePackages = {"ua.translate.handler","ua.translate.service.impl"})
#Order(1)
public class AdminSecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
protected CustomSuccessHandler customSuccessHandler;
#Autowired
#Qualifier("customAccessDeniedHandler")
protected AccessDeniedHandler accessDeniedHandler;
#Autowired
#Qualifier("adminDetailsService")
private UserDetailsService uds;
#Override
public void configure(WebSecurity web){
web
.ignoring()
.antMatchers(new String[]{"/resources/**"});
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/bulbular/**")
.authorizeRequests()
.antMatchers("/bulbular/login").permitAll()
.anyRequest().hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/bulbular/login")
.permitAll()
.successHandler(customSuccessHandler)
.failureUrl("/bulbular/login?error")
.usernameParameter("username")
.passwordParameter("password")
.loginProcessingUrl("/j_spring_security_check")
.and()
.logout().deleteCookies("JSESSIONID")
.logoutUrl("/bulbular/logout")
.logoutSuccessUrl("/bulbular/login?logout")
.and()
.csrf()
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler);
}
#Bean
public AuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider impl = new DaoAuthenticationProvider();
impl.setUserDetailsService(uds);
impl.setPasswordEncoder(bcryptEncoder());
impl.setHideUserNotFoundExceptions(false);
return impl;
}
#Bean
public PasswordEncoder bcryptEncoder(){
return new BCryptPasswordEncoder();
}
#Bean(name = "authenticationManager")
public ProviderManager getProviderManager(){
List<AuthenticationProvider> providers = new ArrayList<>();
providers.add(daoAuthenticationProvider());
ProviderManager providerManager = new ProviderManager(providers);
return providerManager;
}
This class is WebSecurityConfigurer implementation in root context, it is base class for two another classes:
#EnableWebSecurity
#ComponentScan(basePackages = {"ua.translate"})
#Order(99)
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
protected CustomSuccessHandler customSuccessHandler;
#Autowired
#Qualifier("customAccessDeniedHandler")
protected AccessDeniedHandler accessDeniedHandler;
#Autowired
protected PersistentTokenRepository tokenRepository;
#Autowired
#Qualifier("userDetailsServiceImpl")
protected UserDetailsService uds;
#Override
public void configure(WebSecurity web){
web
.ignoring()
.antMatchers(new String[]{"/resources/**"});
}
#Bean
public AuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider impl = new DaoAuthenticationProvider();
impl.setUserDetailsService(uds);
impl.setPasswordEncoder(bcryptEncoder());
impl.setHideUserNotFoundExceptions(false);
return impl;
}
#Bean(name = "authenticationManager")
public ProviderManager getProviderManager(){
List<AuthenticationProvider> providers = new ArrayList<>();
providers.add(daoAuthenticationProvider());
ProviderManager providerManager = new ProviderManager(providers);
return providerManager;
}
#Bean
public PasswordEncoder bcryptEncoder(){
return new BCryptPasswordEncoder();
}
There are two subclasses, which is located in root context:
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Configuration
#Order(3)
public static class AppSecurityConfigClient extends AppSecurityConfig{
#Override
public void configure(WebSecurity web){
web
.ignoring()
.antMatchers(new String[]{"/resources/**"});
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/client/registration*").anonymous()
.antMatchers("/index","/translators","/orders","/client/login*","/client/confirmation").permitAll()
.antMatchers("/client/**").hasRole("CLIENT")
.and()
.formLogin()
.loginPage("/client/login")
.permitAll()
.successHandler(customSuccessHandler)
.failureUrl("/client/login?error")
.usernameParameter("username")
.passwordParameter("password")
.loginProcessingUrl("/j_spring_security_check")
.and()
.logout().deleteCookies("JSESSIONID")
.logoutUrl("/client/logout")
.logoutSuccessUrl("/client/login?logout")
.and()
/*!!!!Доделать saved request url!!!!*/
.rememberMe().tokenRepository(tokenRepository)
.tokenValiditySeconds(86400)
.and()
.csrf()
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler);
}
}
#Configuration
#Order(2)
public static class AppSecurityConfigTranslator extends AppSecurityConfig {
#Override
public void configure(WebSecurity web){
web
.ignoring()
.antMatchers(new String[]{"/resources/**"});
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/translator/**")
.authorizeRequests()
.antMatchers("/translator/registration*").anonymous()
.antMatchers("/translator/index","/translator/login*","/translator/confirmation").permitAll()
.antMatchers("/translator/**").hasRole("TRANSLATOR")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/translator/login")
.permitAll()
.successHandler(customSuccessHandler)
.failureUrl("/translator/login?error")
.usernameParameter("username")
.passwordParameter("password")
.loginProcessingUrl("/j_spring_security_check")
.and()
.logout().deleteCookies("JSESSIONID")
.logoutUrl("/translator/logout")
.logoutSuccessUrl("/translator/login?logout")
.and()
/**
* Доделать saved request url!!!
*/
.rememberMe().tokenRepository(tokenRepository)
.tokenValiditySeconds(86400)
.and()
.csrf()
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.and()
.userDetailsService(uds);
}
}
}
So, adminDispatcher servlet uses ua.admin.AdminConfig, which in turn scan ua.admin package, and find WebSecurityConfigurerAdapter implementation with second authenticationManager implementation.
/bulbular/ - it is path for this dispathcer servlet, and path for http configuration in WebSecurityConfigurerAdapter implementation. But when I want to log in from /bulbular/login page, spring uses implementatation from SecurityConfig.class - class from root context. Help please!!!!
I was trying to convert the following spring security java configuration to xml configuration;
Java config;
package com.careapple.webservice.security.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.careapple.webservice.security.filter.AuthenticationTokenFilter;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableTransactionManagement
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(userDetailsService)
.passwordEncoder(new BCryptPasswordEncoder());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public AuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
AuthenticationTokenFilter authenticationTokenFilter = new AuthenticationTokenFilter();
authenticationTokenFilter.setAuthenticationManager(super.authenticationManagerBean());
return authenticationTokenFilter;
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated();
// Custom JWT based authentication
httpSecurity
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
}
}
Converted xml configuration; spring-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<bean:beans
xmlns:bean="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.0.xsd">
<global-method-security secured-annotations="enabled"/>
<http realm="Protected API"
create-session="stateless"
entry-point-ref="authenticationEntryPoint"
authentication-manager-ref="authenticationManager">
<csrf disabled="true"/>
<custom-filter ref="authenticationTokenFilter" position="FORM_LOGIN_FILTER"/>
<intercept-url pattern="/login/**" access="permitAll"/>
<intercept-url pattern="/" access="isFullyAuthenticated()"/>
</http>
<bean:bean id="authenticationEntryPoint" class="com.careapple.webservice.security.EntryPointUnauthorizedHandler"/>
<bean:bean id="userDetailService" class="com.careapple.webservice.security.service.UserDetailsServiceImpl"/>
<bean:bean name="bcryptEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
<authentication-manager id="authenticationManager">
<authentication-provider user-service-ref="userDetailService">
<password-encoder ref="bcryptEncoder"></password-encoder>
</authentication-provider>
</authentication-manager>
<bean:bean id="authenticationTokenFilter" class="com.careapple.webservice.security.filter.AuthenticationTokenFilter"
c:authenticationManager-ref="authenticationManager"/>
</bean:beans>
I'm new to spring security, first thing i want is, have i converted the java config to xml correctly. Or have i missed anything.
Next up, if i run the application with this xml file i'm getting the following error;
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.filterChains': Cannot resolve reference to bean 'org.springframework.security.web.DefaultSecurityFilterChain#0' while setting bean property 'sourceList' with key [0]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.web.DefaultSecurityFilterChain#0': Cannot resolve reference to bean 'authenticationTokenFilter' while setting constructor argument with key [3]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'authenticationTokenFilter' defined in ServletContext resource [/WEB-INF/spring-security.xml]: Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)
Where, authenticationTokenFilter is a custom filter that i'm using and it implements UsernamePasswordAuthenticationFilter of spring.
This is my web.xml file;
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-security.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>authenticationTokenFilter</filter-name>
<filter-class>com.careapple.webservice.security.filter.AuthenticationTokenFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>authenticationTokenFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
I want too things now,
1. Whether all my configuration xml files are correct.
2. If so how to rectify the exception i'm getting.
Please give your suggestions, i'm struggling with this for a long time. I referred all the related stackoverflow question but i was not able to find a solution from them.
Edit:
This is my AuthenticationTokenFilter;
package com.careapple.webservice.security.filter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.careapple.webservice.security.TokenUtils;
import com.careapple.webservice.util.AppConstant;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class AuthenticationTokenFilter extends UsernamePasswordAuthenticationFilter {
#Autowired
private TokenUtils tokenUtils;
#Autowired
private UserDetailsService userDetailsService;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException{
tokenUtils = WebApplicationContextUtils
.getRequiredWebApplicationContext(this.getServletContext())
.getBean(TokenUtils.class);
userDetailsService = WebApplicationContextUtils
.getRequiredWebApplicationContext(this.getServletContext())
.getBean(UserDetailsService.class);
HttpServletResponse resp = (HttpServletResponse) response;
resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH");
resp.setHeader("Access-Control-Max-Age", "3600");
resp.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, " + AppConstant.tokenHeader);
HttpServletRequest httpRequest = (HttpServletRequest) request;
String authToken = httpRequest.getHeader(AppConstant.tokenHeader);
System.out.println("Token: " + authToken);
String username = this.tokenUtils.getUsernameFromToken(authToken);
System.out.println("Username: " + username);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
System.out.println("inside first if");
if (this.tokenUtils.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
try {
chain.doFilter(request, response);
} catch (Exception e) {
Map<String, String> tokenExpired = new HashMap<>();
tokenExpired.put("TokenExpired", "Authentication token expired, please login again");
resp.setContentType("application/json");
resp.setStatus(HttpStatus.FORBIDDEN.value());
resp.getWriter().write(convertObjectToJson(tokenExpired));
}
}
public String convertObjectToJson(Object object) throws JsonProcessingException {
if (object == null) {
return null;
}
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(object);
}
}
Your custom authentification filter needs to be defined only on the spring-security.xml file, looking at it, it should be fine.
On your web.xml, you need to remove your filter (it's a Spring Security filter not a JavaEE filter) and add the spring security filter. The error message from Spring is that it cannot find the Spring Security filter and thus refusing the parse the pring-security.xml file.
So remove your filter from web.xml and add the spring security with these lines and all should be fine
<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>
Regards,
Loïc
Hi I am new to Spring Security and Spring Oauth, i am configuring a Spring Oauth Server and Resource Server java based. but i need to configure some of the web mvc based on xml, like need to initialize servlet parameters.
below is my Web.xml
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Spring Web MVC Application</display-name>
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Spring Security -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>contextAttribute</param-name>
<param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
and mvc-dispatcher-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" 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.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<context:component-scan base-package="com.resource" />
<mvc:annotation-driven />
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/userbase" />
<property name="username" value="username" />
<property name="password" value="password" />
</bean>
</beans>
and Error log is below:
HTTP Status 500 - No WebApplicationContext found: no ContextLoaderListener registered?
type Exception report
message No WebApplicationContext found: no ContextLoaderListener registered?
description The server encountered an internal error that prevented it from fulfilling this request.
exception
java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered?
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:252)
note The full stack trace of the root cause is available in the Apache Tomcat/7.0.68 logs.
Apache Tomcat/7.0.68
SecurityConfiguration.java
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(passwordEncoder)
.usersByUsernameQuery(
"select username,password, enabled from users where username=?")
.authoritiesByUsernameQuery(
"select username, authority from authorities where username=?");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
****OAuth2ServerConfig.java****
import java.security.Principal;
import java.util.UUID;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.web.bind.annotation.RequestMapping;
#Configuration
public class OAuth2ServerConfig {
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Autowired
private DataSource dataSource;
public static final String RESOURCE_ID = "resource_id";
#Bean
public JdbcTokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Bean
protected AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
#Override
public void configure(ResourceServerSecurityConfigurer resources)
throws Exception {
resources.tokenStore(tokenStore());
resources.resourceId(RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
}
#RequestMapping("/")
public String getInfo() {
return "id: " + UUID.randomUUID() + " content : Spring Oauth2 Welcome first Page";
}
#RequestMapping("/user")
public Principal getUser(Principal principal) {
return principal;
}
}
Please help me on above.
It looks like contextAttribute suffix of the springSecurityFilterChain must match the dispatcher servlet name. Here in your case i see the contextAttribute suffix is dispatcher but the dispatcher servlet name is mvc-dispatcher. Can you try matching them i.e change the contextAttibute suffix to mvc-dispatcher as below and see if the issue goes away.
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>contextAttribute</param-name>
<param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.mvc-dispatcher</param-value>
</init-param>
</filter>
I got this info from https://www.txedo.com/blog/spring-security-no-contextloaderlistener-registered/