I'm trying to implement JWT in my Spring Boot application. I implemented WebSecurityConfig like this :
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.authorizeRequests().antMatchers("/login/*").permitAll().
anyRequest().authenticated().and().
exceptionHandling().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
Inside controller I have just a few end-points, just for testing like this :
#RestController("/login")
public class LoginController {
#PostMapping("/signup")
private void signup(#RequestBody LoginCredential loginCredential){
System.out.println("signup");
}
#PostMapping("/signin")
private void signin(#RequestBody LoginCredential loginCredential){
System.out.println("signin");
}
#GetMapping("/test")
private String test(){
return "test, working ?";
}
}
But when I posted some data in http://localhost:8080/login/signup it says 404.
Then I wrote a filter/interceptor to check where the requests end to.
And I found this : http://localhost:8080/login/signup
By : System.out.println(request.getRequestURL().toString());
I think there is some problem in my WebSecurityConfigurerAdapter in the antMatcher.
How can I resolve this problem ?
you are getting 404 because your path is not correct, the correct path is
http://localhost:8080/signup
because as per docs '/login' is logical component name.
If you wish to make this URL work
http://localhost:8080/login/signup
then you have to add RequestMapping to RestController hence you controller should look something like this:
#RestController
#RequestMapping("/login")
public class LoginController {
...
}
Related
I've been following a spring security example but I cannot make sense of it. A simple RestController replying hello on a GetMapping("/hello") with a 200 status code. Once I change it to a PostMapping I receive a 401 for the same credentials sent.
Seems I am missing something fundamental here as I would expect both requests to return a 200 status code.
The security config:
#Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {
#Override
#Bean
public UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager(
List.of(
User.withUsername("john")
.password("12345")
.authorities("ROLE_ADMIN")
.build(),
User.withUsername("jane")
.password("12345")
.authorities("ROLE_MANAGER")
.build()
)
);
}
#Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic()
.and()
.authorizeRequests()
.anyRequest()
.hasRole("ADMIN");
}
}
The RestController with the following get mapping returns 200 for this call:
curl -v -u john:12345 localhost:8080/hello
and this mapping:
#RestController
public class HelloController {
#GetMapping("/hello")
public String hello() {
return "Hello!";
}
}
The RestController with the following post mapping returns 401 for this call:
curl -X POST -v -u john:12345 localhost:8080/hello
and this mapping:
#RestController
public class HelloController {
#PostMapping("/hello")
public String hello() {
return "Hello!";
}
}
Spring's CSRF protection comes enabled by default in Spring Security. POST requests are affected by this behavior.
Disable it by doing:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
As pointed out by #Matheus Cirillo, CSRF protection is enabled by default.
It's tempting to want to disable CSRF protection since we're using a restful api, but consider what happens if you're using a browser-based single page application to interact with the server. The same authenticated session is still available in the browser, and the application is still vulnerable to a CSRF attack.
You can find some examples of how to work with csrf protection in your own application in the docs. In a restful api, you can also provide an endpoint that returns the csrf token in a header or a response parameter.
I have the following spring boot 2.0 config but I am still getting the basic auth login screen. I DO NOT want to disable all spring security like almost every post on the internet suggests. I only want to stop the form login page and basic auth so I can use my own.
I have seen all the suggestions with permitAll and exclude = {SecurityAutoConfiguration.class} and a few others that I can't remember anymore. Those are not what I want. I want to use spring security but I wan my config not Spring Boots. Yes I know many people are going to say this is a duplicate but I disagree because all the other answers are to disable spring security completely and not just stop the stupid login page.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class CustomSecurity extends WebSecurityConfigurerAdapter {
private final RememberMeServices rememberMeService;
private final AuthenticationProvider customAuthProvider;
#Value("${server.session.cookie.secure:true}")
private boolean useSecureCookie;
#Inject
public CustomSecurity(RememberMeServices rememberMeService, AuthenticationProvider customAuthProvider) {
super(true);
this.rememberMeService = rememberMeService;
this.bouncerAuthProvider = bouncerAuthProvider;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v2/**").antMatchers("/webjars/**").antMatchers("/swagger-resources/**")
.antMatchers("/swagger-ui.html");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable().formLogin().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).headers().frameOptions().disable();
http.authenticationProvider(customAuthProvider).authorizeRequests().antMatchers("/health").permitAll()
.anyRequest().authenticated();
http.rememberMe().rememberMeServices(rememberMeService).useSecureCookie(useSecureCookie);
http.exceptionHandling().authenticationEntryPoint(new ForbiddenEntryPoint());
}
}
If you want to redirect to your own login page, i can show your sample code and configuration
remove the http.httpBasic().disable().formLogin().disable();, you should set your own login page to redirect instead of disable form login
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/my_login").permitAll().and().authorizeRequests().anyRequest().authenticated();
http.formLogin().loginPage("/my_login");
}
then create your own LoginController
#Controller
public class LoginController {
#RequestMapping("/my_login")
public ModelAndView myLogin() {
return new ModelAndView("login");
}
}
you can specified the login with thymeleaf view resolver
I am currently trying to test out Okta with SPA front end (Create-React-App) and a Spring Boot application.
Currently I have the apps working, in that a user logins on the front end (via okta). The user can then access protected resources from server (spring boot). Hence the integration works well and nice.
My issue is I can't access the Principal on my Rest Controller.
ENV
Note: Spring-Security-Starter is NOT on the classpath just the OAuth2 autoconf
Spring Boot 2.0.6.RELEASE
okta-spring-boot-starter:0.6.1
spring-security-oauth2-autoconfigure:2.0.6.RELEASE'
Spring Configuration
okta.oauth2.issuer=https://dev-886281.oktapreview.com/oauth2/default
okta.oauth2.clientId={ clientId }
okta.oauth2.audience=api://default
okta.oauth2.scopeClaim=scp
okta.oauth2.rolesClaim=groups
security.oauth2.resource.user-info-uri=https://dev-886281.oktapreview.com/oauth2/default/v1/userinfo
Okta Service Configuration
Application type: Single Page App (SPA)
Allowed grant types: Implicit
Allow Access Token with implicit grant type: true
Controller
#RestController
#RequestMapping("/products")
public class ProductController {
...
#GetMapping
public ResponseEntity<List<ProductEntity>> getAllProducts(Principal principal) {
SpringBoot
#EnableResourceServer
#SpringBootApplication
public class CartyApplication {
public static void main(String[] args) {
SpringApplication.run(CartyApplication.class, args);
}
#EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
#Bean
protected ResourceServerConfigurerAdapter resourceServerConfigurerAdapter() {
return new ResourceServerConfigurerAdapter() {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
.anyRequest().authenticated();
}
};
}
Once again the overall integration is working fine, users can only access protected resources once they've signed in via okta, I'm just wondering how to get the users details from okta on the controller.
Thanks in advance.
P.S soz for the code dump
EDIT: Removed snippets and added full CartyApplication class
EDIT2: Added repo - https://github.com/Verric/carty-temp
I have a feeling you might be missing this:
#EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
I'm guessing should remove the .antMatchers("/**").permitAll() line.
See: https://docs.spring.io/spring-security/site/docs/current/reference/html/jc.html#CO3-2
I'm guessing you want to protect all/most of your endpoints? I'd recommend only allowing specific routes, and protecting everything else.
I understand #EnableWebSecurity disables all spring security defaults, therefore I have overridden the required methods in WebSecurityConfigurerAdapter. However, no matter what I do css and all other static assets get a 403 or 405.
Using spring boot 2.0.0.M7 with spring security created from https://start.spring.io/
Folder structure is the normal
- resources
- static
- css
styles.css
web.ignoring() doesn't do anything for some reason, yet when I enable debugging it does mention that the below paths have been bypassed but I still get a 405.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/webjars/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
authorizeRequests()
.antMatchers("/register").permitAll()
.anyRequest().authenticated();
}
}
For further debugging I have even tried to permit everything by doing the below, but every url is still denied which is extremely confusing and makes me think there is some key concept I am not grasping.
http.authorizeRequests().antMatchers("/**").permitAll()
Finally, I have also tried to implement WebMvcConfigurer with various combinations of locations which don't work either.
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("/webjars/");
}
Can anyone help me?
All the above security configuration is actually correct. You don't have to implement WebMvcConfigurer, only extend WebSecurityConfigurerAdapter.
Turns out to be a very hard bug to track down. I had a controller that served up a registration form like this.
#Controller
public class RegistrationController {
#GetMapping("/register")
public String getRegisterView(Model model) {
model.addAttribute("registerDto", new RegisterDto());
return "register";
}
#PostMapping
public String register(#Valid #ModelAttribute("registerDto") RegisterDto registerDto, BindingResult result) {
// business logic...
return "register";
}
}
The bug is the in the #PostMapping where I forgot to include the path!! which causes spring all sorts of issues when mapping paths. It would be nice if these annotations threw exceptions if no path was provided.
To fix this I updated it to #PostMapping("/register") and now all paths inside
web.ignoring().antMatchers("/css/**", "/js/**", "/webjars/**"); are allowed through.
So ensure all your contoller route annotations have paths in them!
I'm trying to set up a RESTful API with Spring Boot and I'm trying to enable basic authentication. How come I keep hitting a 403 Access Denied error? I'm sending my credentials as a header in Postman (see image attached). If I remove .anyRequest.authenticated(), it works fine. I don't want to remove that though because I would like basic authentication for every endpoint. What am I doing wrong?
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
SecurityConfiguration.java
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/h2-console/*").permitAll()
.anyRequest().authenticated();
http.csrf().disable();
http.headers().frameOptions().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
Controller.java
#RestController
public class Controller {
#RequestMapping("/test")
public String index() {
return "Greetings from Spring Boot!";
}
}
After digging around in the Spring docs, it seems I understand what each of the chained method calls are for.
Anyway, the simple answer is that I needed .and().httpBasic() to enable Basic HTTP Authentication over my REST API.
.anyRequest().authenticated() simply mandates that every request is authenticated, but did not specify what method. Adding basic authentication means we can use basic auth to authenticate a user.
See more.