In my spring app, I am using Spring 4.0.4 and Spring Security 3.2.3. I copied this code directly from a tutorial on the sprin website but I am having problem compiling because the method registerAuthentication couldn't be overriden from the WebSecurityConfigurerAdapter class and the HttpSecurity class doesn't have the methode authorizeUrls. Is there a jar I'm missing? Or is it the version?
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.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;
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("letsnosh").password("noshing").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeUrls()
.antMatchers("/order/**").hasRole("USER")
.antMatchers("/checkout").hasRole("USER")
.anyRequest().anonymous()
.and()
//This will generate a login form if none is supplied.
.formLogin();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Looks like this is now .authorizeRequests() as of 3.2.0.RC1 . See https://jira.spring.io/browse/SEC-2202
Now it is .authorizeRequests()
I have the same code and it works fine for me. Have you checked that you have both spring-security-web and spring-security-config in your classpath? Try to add them to your dependency section (if you are using maven).
<!-- Spring security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
To get that section of that tutorial to run with gradle, it uses
compile 'org.springframework.security:spring-security-web:3.2.0.M2'
compile 'org.springframework.security:spring-security-core:3.2.0.M2'
compile 'org.springframework.security:spring-security-config:3.2.0.M2'
I'm not sure what later versions would do.
Also, be aware that the .anyRequest().anonymous() line will lock out all pages except order and checkout once your logged in since you're no longer anonymous. Just remove that line for it to work the way the tutorial expects it to.
Try this
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("letsnosh").password("noshing").roles("USER");
}
Related
I am using springdoc-openapi-ui for my web application but when I hit http://localhost:8080/swagger-ui/index.html in the browser I am getting 406 error code.
Below is the dependency in pom.xml
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.14</version>
</dependency>
And Spring Security config is
#Configuration
#EnableWebSecurity
public class SpringSecurityConfig {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/").permitAll();
http.headers().frameOptions().sameOrigin();
return http.build();
}
#Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return web -> web.ignoring().antMatchers("/swagger-ui/**", "/v3/api-docs/**");
}
Spring boot version I am using 2.7.7
Below is network detail via inspect element in chrome for the swagger-ui.config ..
I have spring boot application with starter version 2.1.16, and spring-boot-starter-web dependency. So, i want use javax.ws.rs-api library, and add dependency:
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.1.1</version>
</dependency>
So when i create controller, and add #Path, #Get - i don't get answer from the server (404 not found). How makes it work?
package com.example.test;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.ext.Provider;
#RestController
public class MyController {
//Doesn't work (404 not found
#GET
#Path("/my_test")
public String check() {
return "hi!";
}
//Work
#RequestMapping("/my_test2")
public String check2() {
return "hi2!";
}
}
Add the following dependency in your pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
</dependencies>
if you use spring boot starter version 2.1.16, you will normally have a parent pom which will also define the default version for the above dependency.
Then replace #RestController with #Path("/") from package javax.ws.rs.Path
Now it should be working.
Keep in mind your embedded server now, will be jersey instead of default tomcat.
Edit: Also as found from the author of the question another change is needed to register a ResourceConfig. This is also described here in the official documentation.
In addition to the post above, you need to add the config class:
#Configuration
public class JerseyConfig {
#Bean
public ResourceConfig resourceConfig(MyController myController) {
ResourceConfig config = new ResourceConfig();
config.register(myController);
return config;
}
}
I have added swagger dependencies to the spring boot application and JSON is loading as expected. When I try to load UI by calling this URL http://localhost:9090/swagger-ui.html then the following error is displayed on the browser.
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
There was an unexpected error (type=Not Found, status=404).
pom.xml
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>3.0.0</version>
</dependency>
config class
#Configuration
#EnableSwagger2
public class SwaggerConfig {
}
Ps - My application is running under the port 9090
I found the solution. From Swagger 3.0 we don't need to add 2 dependencies into the build tool. springfox-boot-starter can be replaced instead of those 2 dependencies.
pom.xml
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
Form swagger 3.0 the URL should be http://localhost:9090/swagger-ui/rather than http://localhost:9090/swagger-ui.html
i had similar issue. Make sure you declerate the swagger items.
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//Swagger UI property
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
This must be declareted in the config of implementation with "WebMvcConfigurer".
More information here : https://springfox.github.io/springfox/docs/current/
If you have base url defined, for eg /rest, /api then you will need to put that as well.
My config looks like this:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
#Configuration
#EnableSwagger2
public class SwaggerConfig extends WebMvcConfigurerAdapter{
#Bean
public Docket APIs() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(metadata())
.select()
.apis(RequestHandlerSelectors.basePackage("<rest_controller_package>"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo metadata() {
return new ApiInfoBuilder()
.title("<title_here>")
.description("<description_here>")
.version("BETA")
.build();
}
}
and pom.xml
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
and I access it via http://localhost:9090/api/swagger-ui.html
I think you should add into the SwaggerConfig class a Bean that return a Docket object, which specify the paths and the packages that you want to show in swagger. Something like this:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage(“com.spring.rest.controller”))
.paths(PathSelectors.any())
.build();
}
}
I have a Spring boot application with spring-security-oauth2-autoconfigure. The resource server and authorization server run in the same application.
The application has an user details service globally configure throught WebSecurityConfiguration. It is used to resources' owner authentication.
I have configured the authorization server to authenticate the clients using a in memory store, like that:
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("devglan-client")
.secret("$2a$04$e/c1/RfsWuThaWFCrcCuJeoyvwCV0URN/6Pn9ZFlrtIWaU/vj/BfG")
.authorizedGrantTypes("implicit","refresh_token", "password", "authorization_code")
.scopes("read write trust");
}
The problem:
When I try to get an authorization code using the GET authorize endpoint, then the Spring Security tries to authenticate the application client using the user details service.
I include in the request a HTTP Authorization Header with the Basic option which contains the cliend_id:client_secret credentials.
GET /oauth/authorize?
response_type=code&client_id=bpclient&scope=read HTTP/1.1
Host: localhost:8080
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGV2Z2xhbi1jbGllbnQ6MTIzNDU2
Cache-Control: no-cache
Postman-Token: 77dd0129-bb86-d039-d252-8e7d483092f2
I debugged the code and indetify that DaoAuthenticationProvider tried to retrive the app client credentials using the user details service.
The question
Why the Spring OAuth use the user details service to authenticate the application clients in oauth authorization flow rather than the in-memory configuration?
My code
Pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
Application Main
#SpringBootApplication()
#EnableAutoConfiguration
public class Application extends RepositoryRestConfigurerAdapter{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The Authorization Server configuration:
#Configuration
#EnableAuthorizationServer
public class ServidorAutorizacaoOAuthConfiguracao extends AuthorizationServerConfigurerAdapter {
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("devglan-client")
.secret("$2a$04$e/c1/RfsWuThaWFCrcCuJeoyvwCV0URN/6Pn9ZFlrtIWaU/vj/BfG")
.authorizedGrantTypes("implicit","refresh_token", "password", "authorization_code")
.scopes("read write trust");
}
}
The WebSecurity Configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class SegurancaConfiguracao extends WebSecurityConfigurerAdapter{
#Autowired
private UsuarioServico usuarioServico;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.usuarioServico).passwordEncoder(encoder());
}
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
The resource server configuration:
#Configuration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class ServidorRecursoOAuthConfiguracao extends
ResourceServerConfigurerAdapter {
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.antMatchers("/oauth2/authorization/google", "/login/oauth2/code/google", "/login").permitAll()
.antMatchers("/oauth/authorize").permitAll()
.anyRequest().permitAll()
.and()
.formLogin()
.and()
.oauth2Login()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
Am using JDK 1.8 and Spring Boot 1.2.0 RELEASE.
My pom.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sampleapp</groupId>
<artifactId>sampleapp</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.0.RELEASE</version>
</parent>
<name>Spring Boot Security Example</name>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- HSQLDB -->
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
</dependency>
</dependencies>
</project>
My main app:
package com.sampleapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
My SecurityConfig file:
package com.sampleapp.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/public/**").permitAll()
.antMatchers("/users/**").hasAuthority("ADMIN")
.anyRequest().fullyAuthenticated()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error")
.usernameParameter("email")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.deleteCookies("remember-me")
.logoutSuccessUrl("/")
.permitAll()
.and()
.rememberMe();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
}
There's other classes and interfaces in my sample app but I wanted to keep this post as simple and straightfoward as possible.
When I run this using:
mvn spring-boot:run
Receive the following exceptions:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'securityConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans. factory.BeanCreationException: Could not autowire field: private org.springframework.security.core.userdetails.UserDetailsService com.sampleapp.config.SecurityConfig.userDetailsService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.core.userdetails.UserDetailsService] 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)}
... 52 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.security.core.userdetails.UserDetailsService com.sampleapp.config.SecurityConfig.userDetailsService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.core.userdetails.UserDetailsService] 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.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:558)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
... 74 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.core.userdetails.UserDetailsService] 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:1308)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1054)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:949)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:530)
... 76 more
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.751 s
What am I possibly doing wrong? I checked out this code from:
http://kielczewski.eu/2014/12/spring-boot-security-application/
The only thing I changed was the name of the app to sampleapp along with some packages (namespaces)...
His code works but I wonder why mine doesn't?
EDITED ( for David Hernandez's question):
David, thanks for commenting, I am not sure if I understand your question because the SecurityConfig file uses the org.springframework.security.core.userdetails.UserDetailsService from Spring... In the code that I copied, there is a CurrentUserDetailsService that implements UserDetailService from Spring. If this was your question, here's the code:
package com.sampleapp.service.currentuser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.sampleapp.domain.CurrentUser;
import com.sampleapp.domain.User;
import com.sampleapp.service.user.UserService;
public class CurrentUserDetailsService implements UserDetailsService {
private static final Logger LOG = LoggerFactory.getLogger(CurrentUserDetailsService.class);
private final UserService userService;
#Autowired
public CurrentUserDetailsService(UserService userService) {
this.userService = userService;
}
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
LOG.debug("Authenticating user with email={}", email.replaceFirst("#.*", "#***"));
User user = userService.getUserByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException(String.format("User with email=%s was not found", email)));
return new CurrentUser(user);
}
}
Happy programming!
You need to be sure that you Application.java is in the root of the project
If you refactored the packages
you should have something like that
com
-sampleapp
--other packages
Application.java
By default spring boot use #ComponentScan and scan at the root of the project
If you have something outside your com.sampleapp the #ComponeScan won't be able to see it
You can add your #ComponentScan with your base packages
Ex.
#ComponentScan( basePackages = {"package containing UserDetailsService", "packages containing your app"} )
In your case the ComponentScan is not finding the components so I think it is scanning in the wrong place
Got it working... I was missing #Service for my Service classes.