Secure a ZK Application with Spring Security"
(13 intermediate revisions by 2 users not shown) | |||
Line 2: | Line 2: | ||
= Secure Your Application in Spring's Way= | = Secure Your Application in Spring's Way= | ||
− | [https://spring.io/projects/spring-security Spring Security] is a widely-adopted framework. It can also work with ZK without problems. This doesn't even need zkspring-security. This page will show you how to do it. We assume you know the | + | [https://spring.io/projects/spring-security Spring Security] is a widely-adopted framework. It can also work with ZK without problems. This doesn't even need zkspring-security. This page will show you how to do it. We assume you know the basics of [https://spring.io/projects/spring-boot Spring Boot] and Spring Security. (You can read a Spring Security guide: [https://spring.io/guides/gs/securing-web/ Securing a Web Application] ) So here we just mention those configurations specific to ZK framework. |
+ | |||
+ | The example code mentioned here only works for Spring Security 4/5. | ||
= ZK Spring Boot Starter = | = ZK Spring Boot Starter = | ||
Line 21: | Line 23: | ||
For simplicity, we just register 2 URL mappings: | For simplicity, we just register 2 URL mappings: | ||
− | * < | + | * <code>/login</code>: login page |
− | * < | + | * <code>/secure/{page}</code>: all secure pages |
− | <source lang='java' | + | <source lang='java' highlight='9, 14'> |
@SpringBootApplication | @SpringBootApplication | ||
@Controller | @Controller | ||
Line 45: | Line 47: | ||
</source> | </source> | ||
− | Then put the corresponding zul under < | + | Then put the corresponding zul under <code>web/zul</code> folder. |
[[File:zkspring-zul-path.png | center]] | [[File:zkspring-zul-path.png | center]] | ||
Line 51: | Line 53: | ||
= Web Security Configuration = | = Web Security Configuration = | ||
− | <source lang='java' | + | <source lang='java' highlight='11, 13, 18,19'> |
@Configuration | @Configuration | ||
@EnableWebSecurity | @EnableWebSecurity | ||
Line 69: | Line 71: | ||
.requestMatchers(req -> "rmDesktop".equals(req.getParameter("cmd_0"))).permitAll() // allow desktop cleanup from ZATS | .requestMatchers(req -> "rmDesktop".equals(req.getParameter("cmd_0"))).permitAll() // allow desktop cleanup from ZATS | ||
.mvcMatchers("/","/login","/logout").permitAll() | .mvcMatchers("/","/login","/logout").permitAll() | ||
− | .mvcMatchers("/secure").hasRole("USER") | + | .mvcMatchers("/secure/**").hasRole("USER") |
.anyRequest().authenticated() | .anyRequest().authenticated() | ||
.and() | .and() | ||
Line 94: | Line 96: | ||
* Line 7: We need to disable spring CSRF to make ZK AU pass security filter. But don't worry. [[ ZK%20Developer's%20Reference/Security%20Tips/Cross-site%20Request%20Forgery | ZK already has its own CSRF mechanism]]. | * Line 7: We need to disable spring CSRF to make ZK AU pass security filter. But don't worry. [[ ZK%20Developer's%20Reference/Security%20Tips/Cross-site%20Request%20Forgery | ZK already has its own CSRF mechanism]]. | ||
* Line 13: This line blocks the public access to [[ZK_Developer%27s_Reference/UI_Composing/ZUML/Include_a_Page#Classpath_Web_Resource_Path | ZK class path web resource folder]]. | * Line 13: This line blocks the public access to [[ZK_Developer%27s_Reference/UI_Composing/ZUML/Include_a_Page#Classpath_Web_Resource_Path | ZK class path web resource folder]]. | ||
− | * Line 18-19: Assume we want all pages under < | + | * Line 18-19: Assume we want all pages under <code>/secure</code> are protected and require an authentication. |
= Login Page= | = Login Page= | ||
− | No matter how you design a login page, remember to enclose it with a < | + | No matter how you design a login page, remember to enclose it with a <code><form></code> and the login URL you specify in the web security config. |
− | <source lang='xml' | + | <source lang='xml' highlight='1'> |
<n:form action="/login" method="POST"> | <n:form action="/login" method="POST"> | ||
<grid width="450px"> | <grid width="450px"> | ||
Line 105: | Line 107: | ||
<row spans="2" align="right"> | <row spans="2" align="right"> | ||
<hlayout> | <hlayout> | ||
− | <button type="reset" label="Reset" /> <button type="submit" label="Submit" /> | + | <button type="reset" label="Reset" /> |
+ | <button type="submit" label="Submit" /> | ||
</hlayout> | </hlayout> | ||
</row> | </row> | ||
Line 113: | Line 116: | ||
</source> | </source> | ||
− | = [https://github.com/zkoss/zkspringboot/tree/spring-security | + | = Download Demo Project = |
+ | [https://github.com/zkoss/zkspringboot/tree/master/zkspringboot-demos/zkspringboot-security-demo github - zkoss/zkspringboot - zkspringboot-security-demo] | ||
+ | |||
+ | For an example without springboot (warfile with spring and zk-spring-security), please refer to: | ||
+ | [https://github.com/zkoss/zkspring/tree/master/zkspringessentials/zkspringcoresec github - zkoss/zkspring - zkspringessentials/zkspringcoresec] | ||
+ | |||
+ | = Debug = | ||
+ | Enable debug log in <code>application.properties</code> like | ||
+ | <code>logging.level.org.springframework.security.web=DEBUG</code> if you use spring-boot. | ||
+ | |||
+ | For log4j, you can set | ||
+ | <code>log4j.category.org.springframework.security.web=TRACE</code> | ||
+ | |||
+ | Check what spring security does internally for a request in the log like: | ||
+ | |||
+ | <syntaxhighlight lang='text'> | ||
+ | 2022-11-29 09:29:25 [TRACE] FilterChainProxy:245 - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Mvc [pattern='/login.zul*'], Filters=[]] (1/2) | ||
+ | 2022-11-29 09:29:25 [TRACE] FilterChainProxy:245 - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=any request, Filters=[org.springframework.security.web.session.DisableEncodeUrlFilter@5534e6f1, org.springframework.security.web.context.SecurityContextHolderFilter@4c6fc3e7, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@aa8dce8, org.springframework.security.web.authentication.logout.LogoutFilter@6ad112de, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@18a0721b, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@2ae2fa13, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@66e12c3b, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@44485db, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@1f6f0fe2, org.springframework.security.web.access.ExceptionTranslationFilter@22604c7e, org.springframework.security.web.access.intercept.AuthorizationFilter@4d8f2cfd]] (2/2) | ||
+ | 2022-11-29 09:29:25 [TRACE] FilterChainProxy:245 - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=any request, Filters=[org.springframework.security.web.session.DisableEncodeUrlFilter@5534e6f1, org.springframework.security.web.context.SecurityContextHolderFilter@4c6fc3e7, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@aa8dce8, org.springframework.security.web.authentication.logout.LogoutFilter@6ad112de, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@18a0721b, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@2ae2fa13, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@66e12c3b, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@44485db, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@1f6f0fe2, org.springframework.security.web.access.ExceptionTranslationFilter@22604c7e, org.springframework.security.web.access.intercept.AuthorizationFilter@4d8f2cfd]] (2/2) | ||
+ | 2022-11-29 09:29:25 [DEBUG] FilterChainProxy:223 - Securing POST /zkau | ||
+ | 2022-11-29 09:29:25 [DEBUG] FilterChainProxy:223 - Securing GET /index.zul | ||
+ | |||
+ | </syntaxhighlight> | ||
− | = | + | = Reference = |
− | + | * [https://docs.spring.io/spring-security/site/docs/5.5.8/reference/html5/ Spring Security Reference 5.5.8.RELEASE] | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
{{ZKSpringEssentialsPageFooter}} | {{ZKSpringEssentialsPageFooter}} |
Latest revision as of 01:22, 20 December 2022
Secure Your Application in Spring's Way
Spring Security is a widely-adopted framework. It can also work with ZK without problems. This doesn't even need zkspring-security. This page will show you how to do it. We assume you know the basics of Spring Boot and Spring Security. (You can read a Spring Security guide: Securing a Web Application ) So here we just mention those configurations specific to ZK framework.
The example code mentioned here only works for Spring Security 4/5.
ZK Spring Boot Starter
Spring encourages users to start with Spring Boot. So Please include zk spring boot starter, and it will automatically configure for you with most commonly-used settings.
Spring Boot Starter Security
Follow Securing a Web Application, we add the following elements:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>${springboot.version}</version>
</dependency>
Spring Controller
For simplicity, we just register 2 URL mappings:
/login
: login page/secure/{page}
: all secure pages
@SpringBootApplication
@Controller
public class Application {
public static void main(String[] args) throws Throwable {
SpringApplication.run(Application.class, args);
}
@GetMapping("/login")
public String login() {
return "login";
}
@GetMapping("/secure/{page}")
public String secure(@PathVariable String page) {
return "secure/" + page;
}
}
Then put the corresponding zul under web/zul
folder.
Web Security Configuration
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public static final String ZUL_FILES = "/zkau/web/**/*.zul";
public static final String[] ZK_RESOURCES = {"/zkau/web/**/js/**", "/zkau/web/**/zul/css/**", "/zkau/web/**/img/**"};
// allow desktop cleanup after logout or when reloading login page
public static final String REMOVE_DESKTOP_REGEX = "/zkau\\?dtid=.*&cmd_0=rmDesktop&.*";
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers(ZUL_FILES).denyAll() // block direct access to zul files
.antMatchers(HttpMethod.GET, ZK_RESOURCES).permitAll() // allow zk resources
.regexMatchers(HttpMethod.GET, REMOVE_DESKTOP_REGEX).permitAll() // allow desktop cleanup
.requestMatchers(req -> "rmDesktop".equals(req.getParameter("cmd_0"))).permitAll() // allow desktop cleanup from ZATS
.mvcMatchers("/","/login","/logout").permitAll()
.mvcMatchers("/secure/**").hasRole("USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").defaultSuccessUrl("/secure/main")
.and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/");
}
@Bean
@Override
public UserDetailsService userDetailsService() {
UserDetails user =
User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
- Line 7: We need to disable spring CSRF to make ZK AU pass security filter. But don't worry. ZK already has its own CSRF mechanism.
- Line 13: This line blocks the public access to ZK class path web resource folder.
- Line 18-19: Assume we want all pages under
/secure
are protected and require an authentication.
Login Page
No matter how you design a login page, remember to enclose it with a <form>
and the login URL you specify in the web security config.
<n:form action="/login" method="POST">
<grid width="450px">
...
<row spans="2" align="right">
<hlayout>
<button type="reset" label="Reset" />
<button type="submit" label="Submit" />
</hlayout>
</row>
...
</grid>
</n:form>
Download Demo Project
github - zkoss/zkspringboot - zkspringboot-security-demo
For an example without springboot (warfile with spring and zk-spring-security), please refer to: github - zkoss/zkspring - zkspringessentials/zkspringcoresec
Debug
Enable debug log in application.properties
like
logging.level.org.springframework.security.web=DEBUG
if you use spring-boot.
For log4j, you can set
log4j.category.org.springframework.security.web=TRACE
Check what spring security does internally for a request in the log like:
2022-11-29 09:29:25 [TRACE] FilterChainProxy:245 - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Mvc [pattern='/login.zul*'], Filters=[]] (1/2)
2022-11-29 09:29:25 [TRACE] FilterChainProxy:245 - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=any request, Filters=[org.springframework.security.web.session.DisableEncodeUrlFilter@5534e6f1, org.springframework.security.web.context.SecurityContextHolderFilter@4c6fc3e7, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@aa8dce8, org.springframework.security.web.authentication.logout.LogoutFilter@6ad112de, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@18a0721b, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@2ae2fa13, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@66e12c3b, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@44485db, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@1f6f0fe2, org.springframework.security.web.access.ExceptionTranslationFilter@22604c7e, org.springframework.security.web.access.intercept.AuthorizationFilter@4d8f2cfd]] (2/2)
2022-11-29 09:29:25 [TRACE] FilterChainProxy:245 - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=any request, Filters=[org.springframework.security.web.session.DisableEncodeUrlFilter@5534e6f1, org.springframework.security.web.context.SecurityContextHolderFilter@4c6fc3e7, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@aa8dce8, org.springframework.security.web.authentication.logout.LogoutFilter@6ad112de, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@18a0721b, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@2ae2fa13, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@66e12c3b, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@44485db, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@1f6f0fe2, org.springframework.security.web.access.ExceptionTranslationFilter@22604c7e, org.springframework.security.web.access.intercept.AuthorizationFilter@4d8f2cfd]] (2/2)
2022-11-29 09:29:25 [DEBUG] FilterChainProxy:223 - Securing POST /zkau
2022-11-29 09:29:25 [DEBUG] FilterChainProxy:223 - Securing GET /index.zul
Reference