I have few views made in Vaadin by #Route and now I want to add Security and some Login. In my SecurityConfiguration class I'm setting antMatchers.permitAll() only for 2 views and for the rest with Role ADMIN. But it is not working as I think it should. It demands login to access every view, and after login I have access to all views no matter what role has the user.
I hoped this tutorial will help me, but in there are no views accessible without login.
Securing Your App With Spring Security
My configuration class:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private UserService userService;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Autowired
public SecurityConfiguration(UserService userService) {
this.userService = userService;
}
#Autowired
private void configureAuth(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
auth.inMemoryAuthentication()
.withUser("user")
.password(passwordEncoder().encode("user"))
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and()
.anonymous()
.and()
.authorizeRequests()
.antMatchers("/", "/login").permitAll()
.antMatchers("/recipe-manager", "/ingredient-manager").hasAnyRole("ADMIN")
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.logout().logoutSuccessUrl("/")
.and()
.csrf().disable().cors().disable().headers().disable();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(
"/VAADIN/**",
"/favicon.ico",
"/robots.txt",
"/manifest.webmanifest",
"/sw.js",
"/offline-page.html",
"/icons/**",
"/images/**",
"/frontend/**",
"/webjars/**",
"/h2-console/**",
"/frontend-es5/**", "/frontend-es6/**");
}
}
My Views have annotations like:
#Route("recipe-manager")
public class RecipeManagerView extends VerticalLayout
#Route("")
public class RecipeBrowserView extends VerticalLayout
#Route("login")
public class LoginView extends VerticalLayout
#Route("ingredient-manager")
public class IngredientManagerView extends VerticalLayout
I would expect that anyone can have access to RecipeBrowserView and LoginView, but only logged user can have access to RecipeManagerView and IngredientMangerView.
You cannot use path based matching from Spring Security for Vaadin routes. Spring Security does the matching based on request paths whereas navigation from one view to another inside Vaadin is sent as metadata inside an internal request that always goes to the same hardcoded path.
Instead, you can implement your access control logic in an interceptor provided by Vaadin. You can have a look at https://vaadin.com/tutorials/securing-your-app-with-spring-security to find out more about this.
To my understanding antMatchers only accept single arguments. You should change you configuration class like:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private UserService userService;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Autowired
public SecurityConfiguration(UserService userService) {
this.userService = userService;
}
#Autowired
private void configureAuth(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
auth.inMemoryAuthentication()
.withUser("user")
.password(passwordEncoder().encode("user"))
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and()
.anonymous()
.and()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/recipe-manager", "/ingredient-manager").hasAnyRole("ADMIN")
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.logout().logoutSuccessUrl("/")
.and()
.csrf().disable().cors().disable().headers().disable();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(
"/VAADIN/**",
"/favicon.ico",
"/robots.txt",
"/manifest.webmanifest",
"/sw.js",
"/offline-page.html",
"/icons/**",
"/images/**",
"/frontend/**",
"/webjars/**",
"/h2-console/**",
"/frontend-es5/**", "/frontend-es6/**");
}
}
Related
I want to create a simple login - I already created one, and it worked as is should - but when I start this server, it gives the following output:
2022-04-15 20:02:27.303 INFO 45172 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will not secure any request
This is the corresponding config-file:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final IUserService userService;
#Autowired
public SecurityConfig(IUserService userService){
this.userService = userService;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity.ignoring().antMatchers("/mw_rest_api/**");
}
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeRequests()
.antMatchers("/login", "/register", "/assets/**").permitAll()
.antMatchers("/", "/control-panel", "/control-panel/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.successHandler(loginSuccessHandler())
.failureHandler(loginFailureHandler())
.and()
.logout()
.permitAll()
.logoutSuccessUrl("/login");
}
}
Now I am wondering if I have forgotten something, which I dont see? Or is this a bug of Spring itself?
In configure() method you need to disable cref() then you can give your authorizeRequests() with antMatchers() I thnk every think is fine
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Autowired
private UserDetailsService userDetailsService;
#Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(NoOpPasswordEncoder.getInstance());
return provider;
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests().antMatchers("/login", "/post/blog/**", "/post/viewpost", "/authentication/create").permitAll()
.antMatchers("/post/filter/page/**", "/post/sorted/page/**", "/post/search/page/**").permitAll()
.antMatchers("/authentication/register", "/review/comment/**").permitAll()
.antMatchers("/post/newPost", "/post/publish", "/post/update", "/post/delete").hasAnyAuthority("ADMIN", "AUTHOR")
.antMatchers( "/review/updateComment", "/review/deleteComment").hasAnyAuthority("ADMIN", "AUTHOR", "USER")
.antMatchers("/rest/authenticate", "/rest/blog/**", "/rest/viewpost/**", "/rest/create").permitAll()
.antMatchers("/rest/filter/page/**", "/rest/sorted/page/**", "/rest/search/page/**", "/rest/comment").permitAll()
.antMatchers("/post/register").permitAll()
.antMatchers("/rest/newPost", "/rest/publish", "/rest/update", "/rest/delete").hasAnyAuthority("ADMIN", "AUTHOR")
.antMatchers("/rest/comment/**", "/rest/updateComment/**", "/post/deleteComment/**").hasAnyAuthority("ADMIN", "AUTHOR", "USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/authentication/login").permitAll()
.defaultSuccessUrl("/post/blog")
.and()
.logout().invalidateHttpSession(true)
.clearAuthentication(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/authentication/logout-success").permitAll();
}
}
This is my code you can take it for reference
And I was using jwt for rest API.
Remove permitAll() after logout()
Remove permitAll() after login("...")
So in my Spring boot RESTful API my /logout mapping gets redirected to /login.
I already googled this problem and found a solution, but I do not know how to implement it with my code.
My WebSecurityConfig:
#Configuration
#AllArgsConstructor
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class
WebSecurityConfig extends WebSecurityConfigurerAdapter {//provides security for endpoints
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
private final AccountService accountService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()//So we can send post requests without being rejected(if we using form based indication we want to enable this)
.authorizeRequests()
.antMatchers("/login", "/authenticate")
.permitAll()//any request that goes trough that end point we want to allow.
.anyRequest()
.authenticated().and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login").and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
http.cors();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider =
new DaoAuthenticationProvider();
provider.setPasswordEncoder(bCryptPasswordEncoder);
provider.setUserDetailsService(jwtUserDetailsService);
return provider;
}
This does not work, so I tried to do it like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()//So we can send post requests without being rejected(if we using form based indication we want to enable this)
.authorizeRequests()
.antMatchers("/login", "/authenticate")
.permitAll()//any request that goes trough that end point we want to allow.
.anyRequest()
.authenticated().and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login")
.and().addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
http.cors();
}
But this also does not work.
I know this piece of code :
.and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login")
Should fix my problem but it does not. Can anyone help me?
Thanks!
I'm trying to do my second app using Spring Boot. I have problems with security config even if I use permittAll method. Here's part of my code with WebSecurityConfig:
#Configuration
#EnableWebSecurity(debug=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
MyUserDetailsService userDetailsService;
#Autowired
private PasswordEncoder passwordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/register").permitAll()
.antMatchers("/users").authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Bean
public DaoAuthenticationProvider authenticationProvider(){
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(passwordEncoder);
provider.setUserDetailsService(userDetailsService);
return provider;
}
}
When I use Postman on /register I have 401 Unauthorised, tried to do it many ways but have no idea what to do anymore. Of course, Rest controller handles /register and /users.
Thank you in advance for any help.
In order to use a custom authentification in spring security you got to implement the UserDetailsService interface and override the loadUserByUsername method, such as the example below
public class UserServiceImpl implements UserDetailsService{
#Autowired
private UserDao userDao;
#Override
public UserDetails loadUserByUsername(String useremail)
throws UsernameNotFoundException {
Users user = userDao.findByUserEmail(useremail);
if(user == null){
throw new UsernameNotFoundException("UserName or Password Invalid.");
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), user.getEnabled(), true, true, true, getGrantedAuthorities(userDao.getUserRole(user.getUsersId())));
}
and its working fine for the whole website.
what i want to do now is to expose a restful webservice from the same host and all the requests for that WS will be through the /api/** with a different type of authentification (e.g : using tokens)
is it possible to do it? and if so, is there any idea how to do it ? any useful resources ?
You can start by making security configuration class as follows
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final TokenAuthenticationFilter tokenAuthenticationFilter;
...
public SecurityConfiguration(TokenAuthenticationFilter tokenAuthenticationFilter) {
this.corsFilter = corsFilter;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.and()
.addFilterBefore(tokenAuthenticationFilter, TokenAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(You log out success handler goes here)
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/api/**").authenticated();
}
}
And your TokenAuthenticationFilter class will do the token authenticity for every request.
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final TokenAuthenticationFilter tokenAuthenticationFilter;
...
public SecurityConfiguration(TokenAuthenticationFilter tokenAuthenticationFilter) {
this.corsFilter = corsFilter;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.and()
.addFilterBefore(tokenAuthenticationFilter, TokenAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(You log out success handler goes here)
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/api/**").authenticated();
}
}
I'm trying to define two different security configurations for different url patterns, one of them using form login and another one using basic authentication for an api.
The solution I'm looking for is similar to the one explained here http://meera-subbarao.blogspot.co.uk/2010/11/spring-security-combining-basic-and.html but I would like to do it using java config.
Thanks in advance.
This is the configuration I currently have:
#Configuration
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
#Override
public void configure(WebSecurity web) throws Exception {
// Ignore any request that starts with "/resources/".
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeUrls().antMatchers("/", "/index", "/user/**", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and().formLogin()
.loginUrl("/login")
.failureUrl("/login-error")
.loginProcessingUrl("/security_check")
.usernameParameter("j_username").passwordParameter("j_password")
.permitAll();
http.logout().logoutUrl("/logout");
http.rememberMe().rememberMeServices(rememberMeServices()).key("password");
}
#Bean
public RememberMeServices rememberMeServices() {
TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices("password", userService);
rememberMeServices.setCookieName("cookieName");
rememberMeServices.setParameter("rememberMe");
return rememberMeServices;
}
}
The solution I found was to create another class extending WebSecurityConfigurerAdapter inside the first one, like is described https://github.com/spring-projects/spring-security-javaconfig/blob/master/samples-web.md#sample-multi-http-web-configuration
My solution is as follows:
#Configuration
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
#Override
public void configure(WebSecurity web) throws Exception {
// Ignore any request that starts with "/resources/".
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeUrls().antMatchers("/", "/index", "/user/**", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and().formLogin()
.loginUrl("/login")
.failureUrl("/login-error")
.loginProcessingUrl("/security_check")
.usernameParameter("j_username").passwordParameter("j_password")
.permitAll();
http.logout().logoutUrl("/logout");
http.rememberMe().rememberMeServices(rememberMeServices()).key("password");
}
#Bean
public RememberMeServices rememberMeServices() {
TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices("password", userService);
rememberMeServices.setCookieName("cookieName");
rememberMeServices.setParameter("rememberMe");
return rememberMeServices;
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("api").password("pass").roles("API");
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeUrls()
.antMatchers("/api/**").hasRole("API")
.and()
.httpBasic();
}
}
}
I would say by simply doing it. Specify a second line with authorizeUrls() but for your URLs that are needed with basic authentication. Instead of formLogin() use httpBasic()
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeUrls().antMatchers("/", "/index", "/user/**", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and().formLogin()
.loginUrl("/login")
.failureUrl("/login-error")
.loginProcessingUrl("/security_check")
.usernameParameter("j_username").passwordParameter("j_password")
.permitAll();
http.authorizeUrls().antMatchers("/api/*").hasRole("YOUR_ROLE_HERE").and().httpBasic();
http.logout().logoutUrl("/logout");
http.rememberMe().rememberMeServices(rememberMeServices()).key("password");
}
Something like that should work.
Links: HttpSecurity, HttpBasicConfgurer.
You can solve it by adding .antMatcher("/api/**") just after http in your first config to manage only /api urls. You must have it on the first adapter:
http
.antMatcher("/api/*")
.authorizeRequests()
.antMatchers("^/api/.+$").hasRole("ADMIN")
....
Obviously As Spring updates these tend to fade in their applicability. Running spring cloud starter security 1.4.0.RELEASE this is my solution. My use case was a bit different as I'm trying to secure the refresh endpoint using basic auth for cloud configuration and using a gateway with spring session to pass the authentication in all other instances.
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal1(AuthenticationManagerBuilder auth) throws Exception {
//try in memory auth with no users to support the case that this will allow for users that are logged in to go anywhere
auth.inMemoryAuthentication();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/user").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable();
}
#Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder(11);
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationProvider customAuthenticationProvider;
#Autowired
protected void configureGlobal2(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(customAuthenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/refresh").hasRole("SUPER")
.and()
.csrf().disable();
}
}
}
see the spring security docs for further clarification: Spring Security
The basic idea is that the #Order annotation will dictate what order the auth schemes are run. No #Order means that it is last. If the authorizeRequests() section cannot match on the incoming URL then that configuration will pass and the next one will attempt authentication. This will proceed until authentication succeeds or fails.
The other answers in here are relatively old and for example, I can't find authorizeUrls in Spring 4.
In SpringBoot 1.4.0 / Spring 4, I've implemented the basic/form login like this:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
protected void configure (HttpSecurity aHttp) throws Exception
{
aHttp.authorizeRequests ().antMatchers (("/api/**")).fullyAuthenticated ().and ().httpBasic ();
aHttp.formLogin ()
.loginPage ("/login").permitAll ()
.and ().logout ().permitAll ();
}
}
There may be more ellegant ways of writing this - I'm still working on understanding how this builder works in terms of sequence and so forth. But this worked.