Preface:
There is a part of background management system, which needs to divide the roles of users, and then restrict the access content according to the corresponding permissions of roles; or some portal sites, only special personnel can see the corresponding page information. These all need permission control.
Technical points:
- Spring Boot+Spring Security
Project dependency:
<!--Contain spring-security-web,config,core--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!--web Development related--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
Code:
Entity class:
package com.menghao.security.model.entity; import lombok.Data; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; import java.io.Serializable; /** * <p>System user entity class. < br > * * @author menghao. * @version 2018/3/23. */ @Data @Entity @Table(name = "system_user") public class SystemUser implements Serializable { @Id @GeneratedValue private Long id; private String username; private String password; private String role; }
Configuration class:
package com.menghao.security.web.config; import com.menghao.security.model.entity.SystemUser; import com.menghao.security.service.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import javax.sql.DataSource; import java.util.ArrayList; import java.util.List; /** * <p>Spring-Security Configuration class. < br > * * @author menghao. * @version 2018/3/22. */ @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final DataSource dataSource; // JPA warehouse (code omitted) private final UserRepository userRepository; @Autowired public WebSecurityConfig(@Qualifier("dataSource") DataSource dataSource, @Qualifier("userRepository") UserRepository userRepository) { this.dataSource = dataSource; this.userRepository = userRepository; } /** * Define user authentication logic */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // Three choose one. // memoryMode(auth); // jdbcMode(auth); customizeMode(auth); } /** * Define user authorization logic */ @Override protected void configure(HttpSecurity http) throws Exception { // Define login behavior: allow arbitrary access, login success and failure page address http.formLogin().loginPage("/login").defaultSuccessUrl("/auth/index").failureUrl("/login").permitAll() .and() // Define cookies and their time of existence .rememberMe().tokenValiditySeconds(-1).key("marvel") .and() // Define role MANAGER access path .authorizeRequests().antMatchers("/manager/**").hasRole("MANAGER") .and() // Define role USER access path .authorizeRequests().antMatchers("/user/**").hasRole("USER") .and() // Define public access path .authorizeRequests().antMatchers("/common/**").hasAnyRole("USER", "MANAGER") .and() // Define logout behavior .logout().logoutUrl("/logout").logoutSuccessUrl("/login").permitAll(); } /** * Memory mode * inMemoryAuthentication: Add users to memory and specify permissions */ private void memoryMode(AuthenticationManagerBuilder auth) throws Exception { // You need to specify after Spring Security5, otherwise exceptions will be thrown PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); auth.inMemoryAuthentication() .passwordEncoder(passwordEncoder) .withUser("admin").password(passwordEncoder.encode("admin")).roles("MANAGER") .and().withUser("user").password(passwordEncoder.encode("user")).roles("USER"); } private static final String QUERY_BY_USERNAME = "select U.username,U.password,true from system_user U where U.username = ?"; private static final String QUERY_AUTHORITIES_BY_USERNAME = "select U.username,U.role from system_user U where U.username = ?"; /** * JDBC Pattern * jdbcAuthentication: The default query is implemented in JdbcDaoImpl * * @see UserDetails */ private void jdbcMode(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication().dataSource(dataSource) // You need to specify after Spring Security5, otherwise exceptions will be thrown .passwordEncoder(new BCryptPasswordEncoder()) // Three fields need to be queried: user name, password, and enable .usersByUsernameQuery(QUERY_BY_USERNAME) // Two fields need to be queried: user name and permission .authoritiesByUsernameQuery(QUERY_AUTHORITIES_BY_USERNAME); } @Bean public UserDetailsService customizeUserDetailsService() { // Implementation: loadUserByUsername(String username), return a UserDetails type object return username -> { SystemUser systemUser = userRepository.findByUsername(username); List<GrantedAuthority> authorities = new ArrayList<>(); authorities.add(new SimpleGrantedAuthority(systemUser.getRole())); return new User(systemUser.getUsername(), systemUser.getPassword(), authorities); }; } /** * Custom mode * userDetailsService: Specifies an implementation that returns the UserDetails type */ private void customizeMode(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(customizeUserDetailsService()) .passwordEncoder(new BCryptPasswordEncoder()); } }
Controller:
package com.menghao.security.web.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * <p>Public controller. < br > * * @author menghao. * @version 2018/3/24. */ @RestController @RequestMapping("common") public class CommonController { @RequestMapping("data") public String managePage() { return "this is common data"; } }
package com.menghao.security.web.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * <p>Administrator controller. < br > * * @author menghao. * @version 2018/3/24. */ @RestController @RequestMapping("manager") public class ManagerController { @RequestMapping("data") public String managePage() { return "you are a manager"; } }
package com.menghao.security.web.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * <p>Common user controller. < br > * * @author menghao. * @version 2018/3/24. */ @RestController @RequestMapping("user") public class UserController { @RequestMapping("data") public String managePage() { return "you are an user"; } }
Login page:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <title>Login page</title> <meta name="viewport" content="width=device-width, initial-scale=1"/> <script type="application/x-javascript"> addEventListener("load", function () { setTimeout(hideURLbar, 0); }, false); function hideURLbar() { window.scrollTo(0, 1); } </script> <link th:href="@{/css/style.css}" rel='stylesheet' type='text/css'/> <script th:src="@{/js/jquery.min.js}"></script> </head> <body> <script>$(document).ready(function (c) { $('.close').on('click', function (c) { $('.login-form').fadeOut('slow', function (c) { $('.login-form').remove(); }); }); }); </script> <h1>Login Form</h1> <div class="login-form"> <div class="close"></div> <div class="head-info"> <label class="lbl-1"> </label> <label class="lbl-2"> </label> <label class="lbl-3"> </label> </div> <div class="clear"></div> <div class="avtar"> <img th:src="@{/images/avtar.png}"/> </div> <form th:action="@{/login}" method="post"> <input type="text" class="text" name="username" placeholder="User name"/> <div class="key"> <input type="password" name="password" placeholder="Password"/> </div> <div class="signin"> <input type="submit" value="Login"> </div> </form> </div> </body> </html>
Result:
After the USER of role USER logs in, he can visit http://localhost/user/data and http://localhost/common/data, and display the corresponding string; if he visits http://localhost/manager/data, he will return 403 error page.
The reverse is true for users with the role MANAGER.
Conclusion:
It's very easy to integrate Spring Boot and Spring Security. If you need more fine-grained page permission control, you need to introduce
<dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity4</artifactId> <version>2.1.2.RELEASE</version> </dependency>
In this way, you can use sec:authorize = "hasRole('role'user ') to control the display of page elements.