Im new to these frameworks (Vaadin:7.6.1, Spring Security:4.0.3) and I'm asking myself how to configure the authorized requests if I want to build a Vaadin application.
I looked up a few examples where something like this is written:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
[...]
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.antMatchers("/login**").permitAll()
.antMatchers("/UIDL/**").permitAll()
.antMatchers("/HEARTBEAT/**").authenticated()
.antMatchers("/VAADIN/**").permitAll()
.antMatchers("/resources/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.logout().permitAll()
.and()
.csrf().disable();
}
}
Because I want to design the login page I use the Thymeleaf engine. Therefore I'm using this Controller class:
#Controller
public class LoginController
{
#RequestMapping("/login")
String login(Model model)
{
return "login";
}
}
Which .antMatchers() should I define if I want to block every request of my application if the user isn't logged in? I know that I have to define antMatchers("/resources/**").permitAll() for the login page to get the css and images. But what are these patterns like "/UIDL/**" and what do I need them for?
Which .antMatchers() should I define if I want to block every request
of my application if the user isn't logged in?
If you just want to block every request if the user isn't logged in:
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
.and()
.logout().permitAll()
.and()
.csrf().disable();
}
You don't really need any antMatcher, not even for the login page, as in the .formLogin() part, you already include .permitAll() for that page.
Now for static resources (css, js, images) and with VAADIN in mind, you can do this overriding another method:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/resources/**", "/VAADIN/**");
}
With a Spring Boot project, i also found issues if i didn't allow requests to "/vaadinServlet/**"in the web.ignoring().antMatchers(...).
what are these patterns like "/UIDL/**" and what do I need them for?
When the server receives a request, Spring Security uses these patterns to determine if it should allow or deny access to the request.
They represent the part of the URI after the context root of your application, e.g. in the case of your context root being /, then a request like http://server.com/UIDL/hello the part of the URI that Spring Security will use to determine wether to give acces or not will be /UIDL/hello
The ** represents anything including any sub level, e.g. for the /UIDL/** pattern, the request /UIDL/hello/world/and/any/more/levels will match.
There's also the single * which represents, anything but not including the sub levels, e.g. for the /UIDL/* pattern, the request /UIDL/hello will match, but not /UIDL/hello/world.
As for VAADIN views and UIs, i'm not sure that it is possible to use the antMatchers to grant or deny access, but instead you can annotate the configuration class with #EnableGlobalMethodSecurity(prePost = enabled) and then be able to use the #PreAuthorize( /* spel expression */) annotation on the views to grant or deny access.
UPDATE : Answering to comment questions:
Why do you use the configure(WebSecurity web) method with ignoring the resources instead of the configure(HttpSecurity http) with allowing access? Are there significant differences?
The difference is that WebSecurity#ignoring() makes the request being skipped from the Spring Security filter chain, and it is the recommended way for static resources, anything else than static resources should be processed inside configure(HttpSecurity http).
source
Why do you ignore the "/VAADIN/**" path?
Because that path is used to serve themes, widget sets, and customizations, which is static content, the path is used to serve it dinamycally from the Vaadin jar, but as suggested in the Vaadin documentation, in production environments should be served statically, as it is faster.
source
I could imagine the meaning of "/*" and "/**", but what does "UIDL" and "HEARTBEAT" actually mean? Why are they permitted?
UIDL:
User Interface Definition Language (UIDL) is a language for
serializing user interface contents and changes in responses from web
server to a browser. The idea is that the server-side components
"paint" themselves to the screen (a web page) with the language. The
UIDL messages are parsed in the browser and translated to GWT widgets.
source
Heartbeat requests are performed periodically to verify that the connection is still alive between server and client, or the session haven't expired.
source - see sections 4.8.5, 4.8.6, 4.8.7 and 4.8.8
Related
I export my React build folder to the SpringBoot resources folder and want to serve them statically. I set the antMatcher for my backend endpoints to permit some of urls access,
and rest of urls which are not permitted would need some authentication.
http
.cors().and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/v1/registration","/api/v1/login")
.permitAll()
.anyRequest()
.authenticated().and()
.exceptionHandling().and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
The current situation is that, my frontend React Router URL is also blocked by the antMatcher.
e.g.
http://localhost:8080/login being blocked
I can solve it by just manully hard coding the frontend url into antMatchers, like
.antMatchers("/api/v1/registration","/api/v1/login", "/login","/registration"...)
.permitAll()
but I wonder if there is any elegant way to tell backend not to block frontend url? If the website is growing big, adding URL into antMatcher would be time-consuming.
First of all, we have some basic principles to think about:
Your web server could have some files that should be open to all (such as robots.txt and favicon.ico), on top of the static frontend (which may or may not need to be public without respect to this).
When configuring security, it's better to have a baseline which is to deny all requests and then instead whitelist the requests you want to let through, as opposed to permit all except for a few which you deny. It is easier to forget about paths this way and accidentally allow access to things that should be restricted.
Typically with an open static frontend, the sensitive content is provided from api endpoints which serve the dynamic content (this is NOT the case with a dynamic frontend rendered on the server of course).
An approach that I like is to use multiple ordered configurations for different groups. As far as I know, this can't be accomplished so easily with one configuration, and since complexity is also an enemy of security, this is an approach I like:
#EnableWebSecurity(debug = true)
#Order(1)
class OpenConfig: WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http
.requestMatchers()
.antMatchers("/*", "/locales", "/avatars", "robots.txt")
.and()
.authorizeRequests()
.anyRequest()
.permitAll()
}
}
#EnableWebSecurity
#Order(2)
class RestrictedConfig: WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http
.requestMatchers()
.antMatchers("/api/**")
.and()
.authorizeRequests()
// more fine-grained control within matched group of requests here
.anyRequest()
.authenticated()
.and()
.httpBasic()
}
}
#EnableWebSecurity
#Order(3)
class CatchAllConfig: WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http
.authorizeRequests()
.anyRequest()
.denyAll()
}
}
With .requestMatchers() you determine which requests you want to process in this config and after .authorizeRequests(), you can determine more in details what you want to do with the matched requests. If .requestMatchers() (or a similar method) is omitted, all requests are matched by the configuration. The configurations are looked up in order and the first that matches the request in question is used.
You CAN do something similar to the above with one configuration but I think both that it's easier to think about it structurally when dividing it into multiple configurations and also Spring security seems to behave more consistently (once you involve authentication methods, your choices after .authorizeRequests() can sometimes not have the intended results if you try to control paths in widely different ways).
If you want to do it in the most secure way, then you should explicitly whitelist every static asset that you want public access to, but given that you rebuild the frontend every now and then with new hashes attached to filenames, this becomes tedious. The above is a middle way where you assume that all content that is one level under / belongs to the static frontend and should thus be available without authentication (you could work with file suffixes here as a complement if you want). Any folders under the static frontend must be specifically specified. All you have to do now is make sure that any dynamic content is placed in some path which is secured and that you don't have any packages which automatically adds paths to your application which you don't want to be open and that are added at the root (typically Swagger UI and actuators can do this even though the latter often put them under the /actuator/ path.
I am trying to create a single Spring Boot application (latest version) that can serve:
static HTML, CSS, JavaScript and image (favicon, jpg, png etc) files
HTML content based on Thymeleaf template files
REST endpoints
All of the above should be able to serve:
without authentication (public access)
with authentication (restricted access)
Meaning that the following mappings should apply:
URL request
Served from (resource folder or a controller)
Public
Notes
/ui/media/*
../resources/web/ui/media/*
Yes
/ui/style/*
../resources/web/ui/style/*
Yes
/ui/scrippt/*
../resources/web/ui/scrippt/*
Yes
/ui/login
../resources/web/ui/login.html
Yes
/ui/forgot
../resources/web/ui/forgot.html
Yes
/ui/admin/*
../resources/web/ui/admin/*
No
1
/ui/user/*
../resources/web/ui/user/* and UiUserController using Thymeleaf template files
No
1, 2
/api/auth/login
AuthenticationController::login()
Yes
/api/auth/forgot
AuthenticationController::login()
Yes
/api/ping
ApiPingPongController::ping()
Yes
/api/pong
ApiPingPongController::pong()
No
1
/api/v1/*
WildcardController::handle()
Yes
3
Notes:
Requires user to be authenticated
UiUserController class handles endpoints and uses Thymeleaf template files from resource folder
This single method should be able to handle any request (GET/POST/...) starting with /api/v1/** and based on a hardcoded list of values either can serve for public access or check if JWT token is present and valid (meaning validation should be inside this method. I can validate JWT inhere, so I don't need a solution for that. Just wanted to add it so you know this endpoint is special compared to most examples around.
I've listed lots of endpoints, but only because I haven't been able to combine all of these and security public/non-public and can't find any examples on the Internet that combines all of these.
What I have so far:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and()
.csrf().disable()
.authorizeRequests()
// Public static files
.antMatchers(HttpMethod.GET, "/ui/login", "/ui/forgot", "/ui/media/**", "/ui/style/**", "/ui/script/**").permitAll()
// User static files based on Thymeleaf
.antMatchers(HttpMethod.GET,"/ui/user/**").hasRole("USER")
// Administration static file
.antMatchers(HttpMethod.GET,"/ui/admin/**").hasRole("ADMIN")
// Authentication REST endpoints
.antMatchers(HttpMethod.POST, "/api/auth/login").permitAll()
.antMatchers(HttpMethod.POST, "/api/auth/forgot").permitAll()
// /api/ping, /api/pong endpoints
.antMatchers("/api/ping").permitAll()
.antMatchers("/api/pong").hasAnyRole("USER", "ADMIN")
// /api/v1/** endpoint
.antMatchers("/api/v1/**").permitAll()
.anyRequest().authenticated().and()
// JWT filter
.addFilterAfter(new JWTAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/resources/**");
}
}
The authentication process and REST endpoints works, but the Thymeleaf endpoint can not find resource files and the static files are not accessible at all (even public or authenticated).
I've tried so many combinations for the last 3 weeks that I am really close to giving up on this.
Can someone point me in the right direction?
add this in your webSecurityConfig class
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/resources/**", "/static/**", "/css/**");
}
I have override the configure method of WebSecurityConfigurerAdapter class as:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin();
}
I have APIs as /admin, /admin/user, /admin/user/test. When i login as admin i can access all the three URLs. I just wanted to know the difference between '/admin/**' and '/admin',
In case of /api/**, hasRole(...) will be authorized to all the requests that starts with the pattern /api.
And in case of /api, hasRole(...) will be authorized to only one request i.e. /api
In the above question only the '/admin' request is authorized to 'ADMIN' role. We can also access the other URLs because other URLs just need to be authenticated ignoring the role. We can also access the '/admin/user' or '/admin/user/test' while logging with user. If we have used antPattern as '/admin/**', then we won't be able to access those APIs through the session of user.
I am new to Spring Security and i was about to post the question but after spending some time, i came to know a little about it, so i also included my understanding for suggestions.
My application has an authenticated admin area. My problem is that it also requires authentication for the login page (although it's marked as either "anonymous" or "permitAll" - I've tried both).
My configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/admin/**")
.authorizeRequests()
.antMatchers("/admin/login.html", "/admin/logout.html").permitAll() //"anonymous()" has same result
.antMatchers("/admin/**").hasAnyRole("ADMIN", "PUBLISHER")
.anyRequest().authenticated()
.and()
.addFilter(preAuthenticationFilter())
.addFilter(adminExceptionTranslationFilter())
.csrf().disable()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/admin/logout.html"))
.logoutSuccessUrl("/index.html")
.invalidateHttpSession(true);
}
The only thing I could think of that might be the culprit is the preAuthenticationFilter which extends the AbstractPreAuthenticatedProcessingFilter class (the authentication is smart card based and that class extracts the credential from a certificate sent by the browser). I'm guess that maybe because it's a type of preAuthenticated filter, then maybe Spring runs it before any request - thus prompting the authentication request in the browser (even if the accessed page "/admin/login.html" doesn't require authentication).
So my question ultimately is how do I actually disable authentication for the login page? As far as I can tell from the documentation, the antMatchers are configured correctly.
It was a proxy problem. The code in question is correct.
I read a lot of manual of how to configure spring security, but still stuck with configuration.
So i want to configure rest calls and other http calls. As i understand i can create urls like /server/** - for web application and /rest/** - for rest application. For any call of web application urls i want to create a login page (when user not authenticated), but for rest app, i want to fire Unauthorised code.
I do it with spring annotations by extend WebSecurityConfigurerAdapter
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/").access("hasRole('ROLE_USER')")
.antMatchers("/server/*").access("hasRole('ROLE_USER')")
.and()
.formLogin().loginPage("/login").permitAll().failureUrl("/login?error")
.usernameParameter("username")
.passwordParameter("password")
.and()
.exceptionHandling()
.accessDeniedPage("/accessDenied");
}
for server it works fine, but if i try to add /rest here when i try to call /rest/[something] (in browser), it always forwards me to /login page.
I don't understand why and it is break my mind.
Thanks for any helpful responses.
You have this:
.antMatchers("/").access("hasRole('ROLE_USER')")
.antMatchers("/server/*").access("hasRole('ROLE_USER')")
.and()
.formLogin()
means / access need ROLE_ACCESS and for authentication, direct to formLogin
You need multiple authentication conf.
see http://docs.spring.io/autorepo/docs/spring-security/4.0.0.CI-SNAPSHOT/reference/htmlsingle/
And in one of them you need something like this
http
.antMatcher("/rest/**")
.exceptionHandling().authenticationEntryPoint(new AccessDenyEntryPoint()).and()
.authorizeRequests().antMatchers("/spring/**").denyAll();
You have to add one more .antMatchers("/rest/*").permitAll() if you don't want to validate your rest urls.