Lab M02P05
Spring Security supports web applications, which needs to provide User authentication functionality and to protect web application from unauthorized access. This example utilizes LDAP to store user accounts and roles and demonstrates the simple RBAC access control.
-
Firstly, review the project. It is simple server side (web pages are rendered on backend) web application implemented by Spring MVC, and which uses Thymeleaf templating technology for theHTML5 pages templating and rendering. The src/main/resources folder contains web application static artefacts as well as HTML page templates. Let's start the application and open http://localhost:8080. You should see the table with list of books and few button you can play with. The task is to add authentication and access control to application.
-
Now add the required dependencies of Spring Security and other components. Open the build.gradle file and add following dependencies:
Do not forget to refresh project in IDE, so new dependencies are downloaded. Project uses LDAP for user account storage and Thymeleaf security extension.implementation('org.springframework.boot:spring-boot-starter-security') implementation('org.thymeleaf.extras:thymeleaf-extras-springsecurity6') implementation("org.springframework.security:spring-security-ldap") -
You need to enable and configure security related components. There is SecurityConfig class. Add required annotation @EnableWebSecurity. You've just enabled default Spring Security components which adds login form authentication with default account defined in application.properties (username/password). Restart the application and open http://localhost:8080. you should be redirected to Spring provided default login form like this.

Login with user/password and you should be in. There is no other constraint yet, so the application should work. The Logout button should also work.
-
Let's introduce user account database in LDAP. The Spring supports embedded LDAP server with UnboundID library. The LDAP definition with users is in project resource file user-data.ldif. You need to configure LDAP Authentication provider for Authentication Manager in SecurityConfig
Restart application now. You can try to directly access http://localhost:8080/book/all, but Spring security filter redirects you to login page. Try to login with john/snow, or arya/stark.@Autowired public void configure(AuthenticationManagerBuilder auth) throws Exception { auth .ldapAuthentication() .userDnPatterns("uid={0},ou=people") .groupSearchBase("ou=groups") .contextSource() .url("ldap://localhost:8389/dc=librarymaster,dc=org").managerDn("uid=admin").managerPassword("secret") .and() .passwordCompare() .passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()) .passwordAttribute("userPassword"); } -
It is time for fine tuning of application access. You can configure Spring Security filter chain in order to:
- Provide custom Login form login.html
- application redirects to home page after successful authentication
- session is invalidated upon successful logout
- application redirects to login after successful logout
- Fine grained role based access control to application pages
- common user can browse books and book details
- only admin can add and update Book records
- static resources (css, js, ...) should be excluded from access control
Spring Security utilizes the lambda style configuration. Above can be achieved by following code:
The basic idea is to define URL patterns which should be excluded and included in access control. You can use the Ant style pattern (@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests((authz) -> authz .requestMatchers("/","/webjars/**","/images/**" ,"/css/**","/home/**","/resources/**" ).permitAll() .requestMatchers(RegexRequestMatcher.regexMatcher("/book/[0-9]+/edit")).hasRole("ADMINS") .requestMatchers("/book/**").hasRole("USERS") .anyRequest().authenticated() ).formLogin((login) -> login.loginPage("/login") .successForwardUrl("/home") .permitAll()) .logout((logout) -> logout.invalidateHttpSession(true) .clearAuthentication(true) .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) .logoutSuccessUrl("/login?logout") .permitAll()); return http.build(); }/book/**), or regex pattern (RegexRequestMatcher.regexMatcher("/book/[0-9]+/edit")). Add above code into SecurityConfig and restart the application.The login page should looks differently now.

Try to login as arya/stark, go to Books view and try to click Update button. You should get ACCESS DENIED page. The reason is that updating is permitted for ADMINS only. You can improve the app little bit further as there is no reason to present Update button for common user. Let's fix this in next step.
-
The Thymeleaf enables to access Security Context from HTML pages. So you can customize page content based on user authenticates status and also based on authorization grants. You have
'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'already on project classpath, so let's modify books.html view.- include namespace
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"into<html>tag - locate Update button and add sec:authorize
<td><a class="btn btn-default" sec:authorize="hasRole('ADMINS')" th:href="@{/book/__${item.id}__/edit}">Update</a>in order to show the button only for users in ADMINS role - do the same for Add book button
<a class="btn btn-default" sec:authorize="hasRole('ADMINS')" th:href="@{/book/add}">Add Book</a> - enable Thymeleaf extension in ThymeleafConfig.java,
and add
engine.addDialect(new org.thymeleaf.extras.springsecurity6.dialect.SpringSecurityDialect());
Now , restart the application and check if it works as expected. First login as john/snow who is admin, and then as arya/stark who is common user. You can notice the Login and Logout buttons are displayed based on authentication status. Review header.html code and search for
sec:authorize. - include namespace
-
There is also demonstration how to map error pages for various HTTP statuses. If you need to present customized error message for user for HTTP 403 Not Authorized, just design page and add it into templates/error folder. Spring recognizes the html file named as 403.html in this case.