I am using Spring 4.0.5.RELEASE and Spring Security 3.2.4.
I am trying to create a simple sample app using java config (based on the Spring samples). The app starts up and the authentication works correctly, that is, I am redirected to a login form when accessing protected url /settings/profile
However there is no /logout url generated? if I hit localhost:8080/logout I get a 404.
I've used similar code on a previous project, so maybe has something to do with versions?
Heres my Security Config
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
auth.inMemoryAuthentication().withUser("admin").password("password").roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/settings/**").hasRole("ROLE_ADMIN")
.and()
.formLogin()
.and()
.logout()
.deleteCookies("remove")
.invalidateHttpSession(true)
.logoutUrl("/logout")
.logoutSuccessUrl("/logout-success")
.permitAll();
}
}
Here is my WebAppInitializer to bootstrap the app
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { SecurityConfig.class , MvcConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
}
and finally my MvcConfig
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = {"web"})
public class MvcConfig extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
By default POST request is required to the logout url. To perform logout on GET request you need:
http
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
Or if you want to support PUT or other method, pass this as a parameter:
http
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "PUT"));
See the Docs: http://docs.spring.io/spring-security/site/docs/3.2.4.RELEASE/reference/htmlsingle/ (section 6.5.3. Logging Out)
Related
I am new to Spring Security and Oauth2. In my spring boot application, I have implemented authentication with Oauth2 with following set of changes:
Custom Ouath2 User service is as follows:
#Component
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private UserRepository userRepository;
#Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
...
}
}
Security Configuration is as follows:
#EnableWebSecurity
#Import(SecurityProblemSupport.class)
#ConditionalOnProperty(
value = "myapp.authentication.type",
havingValue = "oauth",
matchIfMissing = true
)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final CustomOAuth2UserService customOAuth2UserService;
public SecurityConfiguration(CustomOAuth2UserService customOAuth2UserService) {
this.customOAuth2UserService = customOAuth2UserService;
}
#Override
public void configure(WebSecurity web) {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/bundle.js")
.antMatchers("/slds-icons/**")
.antMatchers("/assets/**")
.antMatchers("/i18n/**")
.antMatchers("/content/**")
.antMatchers("/swagger-ui/**")
.antMatchers("/swagger-resources")
.antMatchers("/v2/api-docs")
.antMatchers("/api/redirectToHome")
.antMatchers("/test/**");
}
public void configure(HttpSecurity http) throws Exception {
RequestMatcher csrfRequestMatcher = new RequestMatcher() {
private RegexRequestMatcher requestMatcher =
new RegexRequestMatcher("/api/", null);
#Override
public boolean matches(HttpServletRequest request) {
return requestMatcher.matches(request);
}
};
http.csrf()
.requireCsrfProtectionMatcher(csrfRequestMatcher)
.and()
.authorizeRequests()
.antMatchers("/login**").permitAll()
.antMatchers("/manage/**").permitAll()
.antMatchers("/api/auth-info").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/info").permitAll()
.antMatchers("/management/prometheus").permitAll()
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
.anyRequest().authenticated()//.and().oauth2ResourceServer().jwt()
.and()
.oauth2Login()
.redirectionEndpoint()
.baseUri("/oauth2**")
.and()
.failureUrl("/api/redirectToHome")
.userInfoEndpoint().userService(oauth2UserService())
;
http.cors().disable();
}
private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
return customOAuth2UserService;
}
}
Content of application.properties is as follows:
spring.security.oauth2.client.registration.keycloak.client-id=abcd
spring.security.oauth2.client.registration.keycloak.client-name=Auth Server
spring.security.oauth2.client.registration.keycloak.scope=api
spring.security.oauth2.client.registration.keycloak.provider=keycloak
spring.security.oauth2.client.registration.keycloak.client-authentication-method=basic
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
myapp.oauth2.path=https://internal.authprovider.com/oauth2/
spring.security.oauth2.client.provider.keycloak.token-uri=${myapp.oauth2.path}token
spring.security.oauth2.client.provider.keycloak.authorization-uri=${myapp.oauth2.path}authorize
spring.security.oauth2.client.provider.keycloak.user-info-uri=${myapp.oauth2.path}userinfo
spring.security.oauth2.client.provider.keycloak.user-name-attribute=name
myapp.authentication.type=oauth
Now, with the existing authentication mechanism, I would like to add support for multiple authentication providers: LDAP, Form-Login, etc.
In this regard, I have gone through a few articles:
https://www.baeldung.com/spring-security-multiple-auth-providers
Custom Authentication provider with Spring Security and Java Config
But, I am not getting any concrete idea regarding what changes should I do in the existing code base in order to achieve this.
Could anyone please help here? Thanks.
I've created a simplified setup starting from your code with support for both OAuth2 and Basic Auth.
/tenant2/** will start a basic authentication.
/** (everything else) triggers an OAuth2 Authorization Code authentication.
The key to achieve this is to have one #Configuration class per authentication type.
Let's start with the controllers:
Tenant1HomeController
#Controller
public class Tenant1HomeController {
#GetMapping("/tenant1/home")
public String home() {
return "tenant1Home";
}
}
Tenant2HomeController
#Controller
public class Tenant2HomeController {
#GetMapping("/tenant2/home")
public String home() {
return "tenant2Home";
}
}
Now, the configuration classes:
Tenant1SecurityConfiguration
#Configuration
public class Tenant1SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login**").permitAll()
.antMatchers("/manage/**").permitAll()
.antMatchers("/api/auth-info").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/info").permitAll()
.antMatchers("/management/prometheus").permitAll()
.antMatchers("/management/**").hasAuthority("ADMIN")
.antMatchers("/tenant1/**").authenticated()
.and()
.oauth2Login()
.and()
.cors()
.disable();
}
}
Tenant2SecurityConfiguration (Notice the #Order(90), that's important
#Order(90)
#Configuration
public class Tenant2SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(new AntPathRequestMatcher("/tenant2/**"))
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/tenant2/**").hasAuthority("BASIC_USER")
.and()
.httpBasic();
http.cors().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.password("{noop}password")
.roles("BASIC_USER");
}
}
Finally the configuration:
spring:
security:
oauth2:
client:
registration:
keycloak:
client-id: myclient
client-secret: c6dce03e-ea13-4b76-8aab-c876f5c2c1d9
provider:
keycloak:
issuer-uri: http://localhost:8180/auth/realms/myrealm
With this in place, if we hit http://localhost:8080/tenant2/home, will be prompted with the basic auth popup:
Trying with http://localhost:8080/tenant1/home sends you to Keycloak's login form:
UPDATE:
It's completely viable to configure a multitenant application with the configuration above.
The key would be that each authentication provider works with a different set of users (tenants), e.g.:
TENANT 1 (OAuth2 authentication):
#Configuration
public class Tenant1SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
...
.and()
.oauth2Login()
.and()
...
This first subset of users is federated by the OAuth2 provider, Keycloak in this case.
TENANT 2 (Basic / form /xxx authentication):
#Order(90)
#Configuration
public class Tenant2SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
...
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(XXX)
For the second tenant, you can use a userDetailsService that points to a different repository of users (LDAP, database...).
I want to create a simple REST API, I am using Angular 6 and Spring Boot. I wanted to implement logging to my app but whenever I try to sign up I get 404 Not Found /login. I have looked up to existing similar issues I found but nothing seems to help me.
Here's my spring security configuration ( I am sure that my sign up form and controller works properly, so I guess the issue lies somewhere down there)
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${allowedOriginAddress}")
private String allowedOriginAddress;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public UserDataDetailsService userDataDetailsService() {
return new UserDataDetailsService();
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(
Arrays.asList(
allowedOriginAddress,
allowedOriginAddress + "/*")
);
configuration.setAllowedMethods(Arrays.asList("GET","POST","DELETE","OPTIONS","PUT"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Arrays.asList("*"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
.authorizeRequests()
.anyRequest().permitAll()
.and().csrf().disable()
.formLogin()
.loginPage("/")
.usernameParameter("email")
.loginProcessingUrl("/login")
.failureForwardUrl("/prev")
.successForwardUrl("/next")
.and().authorizeRequests().anyRequest().permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDataDetailsService()).passwordEncoder(passwordEncoder());
}
}
#SpringBootApplication
#Configuration
public class WhispererApplication implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(WhispererApplication.class, args);
}
}
I cannot find the authentication configuration in your project. Please try adding the next code:
.anyRequest().authenticated()
I think your code will be:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.cors().and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/public/**").permitAll()
.antMatchers(HttpMethod.POST, "/login").permitAll()
.anyRequest().authenticated().
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()));
}
The JWTAuthenticationFilter and JWTAuthorizationFilter are to manage the JWT if you are using that to authenticate the user.
i want to with postman login to my web app which is secure. In the web browser normal i can write my login and password, but how to login by postman when i want to hit under endpoint for example /login ? I should create rest controller which will handle this situation or maybe is the way that automatically handle this situation? Is it a good idea to send username and password in url something like that /login?username=admin&password=admin or better in body?
Below it's my security config:
SecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private MyUserDetailsService userDetailsService;
#Autowired
private UserRepository userRepository;
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder(11);
}
#Override
protected UserDetailsService userDetailsService() {
return userDetailsService;
}
#Bean
public SimpleSocialUserDetailsService simpleSocialUserDetailsService() {
return new SimpleSocialUserDetailsService(userRepository);
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.csrf().disable()
.headers().frameOptions().disable()
.and()
.authorizeRequests()
.antMatchers("/login*", "/success*").anonymous()
.antMatchers("/auth/**", "/signup/**", "/css/*", "/webjars/**","/js/*","/image/*").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login")
.successForwardUrl("/tasks")
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/logout-success").permitAll()
.and()
.apply(new SpringSocialConfigurer());
}
}
What type of security are you using? You can configure Postman to send credentials in the Authorization tab (below example using Basic Auth):
I'm trying to configure a Java-based Spring Security redirect to a login page for any request that is not authenticated and currently have the following configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity security) throws Exception {
security
.ignoring()
.antMatchers("/resources/**")
;
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.formLogin()
.loginPage("/login").permitAll()
.and()
.authorizeRequests()
.anyRequest().authenticated()
;
}
}
In the class that implements WebApplicationInitializer I have the following:
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(SecurityConfig.class);
container.addListener(new ContextLoaderListener(rootContext));
Setting a breakpoint within the configure(HttpSecurity httpSecurity) method shows that the method is called on startup, but no request is redirected to /login.
To answer my own question for others with a similar problem. The solution was to add a new empty class extending AbstractSecurityWebApplicationInitializer as follows:
public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
// Nothing
}
Form based security is redirection to the login page if a user is not authenticated and tries to access a protected action. Instead of the redirect I want it to return HTTP code 403.
As far as I understand, I have to register some kind of entry point for this. Unfortunately I don't undertand how I can set this up for a java based configuration.
This is my security config:
#Configuration
#EnableWebMvcSecurity
pubfooc class FOOSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
//#formatter:off
protected void configure(HttpSecurity http) throws Exception {
http
.authenticationProvider(aS400AuthenticationProvider())
.formLogin()
.loginProcessingUrl("/authorized")
.passwordParameter("password")
.usernameParameter("cfooentId")
.successHandler(foorAuthenticationSuccessHandler())
.failureHandler(foorAuthenticationFailureHandler())
.and()
.csrf().disable()
.rememberMe()
.rememberMeServices(foorRememberMeServices())
.key(CookieService.FOO_SESSION_COOKIE_NAME)
.and()
.sessionManagement().sessionCreationPofoocy(SessionCreationPofoocy.STATELESS)
;
}
//#formatter:on
#Bean
pubfooc FOORememberMeServices foorRememberMeServices() {
return new FOORememberMeServices();
}
#Bean
pubfooc AS400AuthenticationProvider aS400AuthenticationProvider() {
return new AS400AuthenticationProvider();
}
#Bean
pubfooc CookieService cookieService() {
return new CookieService.Impl();
}
#Bean
pubfooc FOOAuthenticationSuccessHandler foorAuthenticationSuccessHandler() {
return new FOOAuthenticationSuccessHandler();
}
#Bean
pubfooc FOOAuthenticationFailureHandler foorAuthenticationFailureHandler() {
return new FOOAuthenticationFailureHandler();
}
}
You should try http.exceptionHandling().authenticationEntryPoint(new org.springframework.security.web.authentication.Http403ForbiddenEntryPoint()) (assuming you are using Spring Security 2.0 or higher).