This article tells the story of Spring Boot integrating Spring Security to implement permission control using annotations on methods, and using a custom UserDetailService to load user information from MySQL. Use the MD5 encryption that comes with Security to encrypt the user's password. The page template uses the thymeleaf engine.
Source code address: https://github.com/li5454yong/springboot-security.git
1. Introduce pom dependencies
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.4.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.34</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.15</version> </dependency> </dependencies>
Here, use druid connection pooling, and Spring Data Jpa implements database access.
2. Configure Spring Security
@Configuration@EnableWebMvcSecurity@EnableGlobalMethodSecurity(prePostEnabled = true)//Enable security annotation public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean @Override protected AuthenticationManager authenticationManager() throws Exception { return super.authenticationManager(); } @Override protected void configure(HttpSecurity http) throws Exception { // Allow all users to access "/" and "/home" http.authorizeRequests() .antMatchers("/", "/home").permitAll() //Access to other addresses requires verification permissions.anyRequest().authenticated() .and() .formLogin() //Specify the login page to be "/login" .loginPage("/login") .defaultSuccessUrl("/hello")//After login successful, jump to "/hello" by default. .permitAll() .and() .logout() .logoutSuccessUrl("/home")//The default url after logging out is "/home" .permitAll(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(customUserDetailsService()) .passwordEncoder(passwordEncoder()); } /** * Set the encryption method of the user password to MD5 encryption* @return */ @Bean public Md5PasswordEncoder passwordEncoder() { return new Md5PasswordEncoder(); } /** * Custom UserDetailsService to read user information from the database* @return */ @Bean public CustomUserDetailsService customUserDetailsService(){ return new CustomUserDetailsService(); }} Only basic configurations are made here, setting up login url, url that jumps after login is successful, and url that jumps to after logout. Using the annotation @EnableGlobalMethodSecurity(prePostEnabled = true) can enable the annotation of security. We can use @PreAuthorize and @PreFilter on methods that require control permissions.
3. Custom userDetailService
public class CustomUserDetailsService implements UserDetailsService { @Autowired //Domain service class private SUserService waitService; @Override public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { //SUser corresponds to the user table in the database, which is the table that ultimately stores the user and password, which can be customized //This example uses the email in SUser as the user name: SUser user = suserService.findUserByEmail(userName); if (user == null) { throw new UsernameNotFoundException("UserName " + userName + " not found"); } // SecurityUser implements UserDetails and maps SUser's Email to username SecurityUser securityUser = new SecurityUser(user); Collection<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>(); authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN")); return securityUser; }}Here you only need to implement the UserDetailsService interface, rewrite the loadUserByUsername method, and retrieve user information from the database. Finally, a UserDetails implementation class is returned.
4. Define the error handling configuration
@Configurationpublic class ErrorPageConfig { @Bean public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){ return new MyCustomizer(); } private static class MyCustomizer implements EmbeddedServletContainerCustomizer { @Override public void customize(ConfigurableEmbeddedServletContainer container) { container.addErrorPages(new ErrorPage(HttpStatus.FORBIDDEN, "/403")); } }}When an access error occurs, jump to "/403".
5. Controller interface
@Controllerpublic class IndexController { @Resource private SUserService sUserService; @RequestMapping("/home") public String home() { return "home"; } @PreAuthorize("hasRole('user')") @RequestMapping(value = "/admin",method = RequestMethod.GET) public String toAdmin(){ return "helloAdmin"; } @RequestMapping("/hello") public String hello() { return "hello"; } @RequestMapping("/login") public String login(){ return "login"; } @RequestMapping("/") public String root() { return "index"; } @RequestMapping("/403") public String error(){ return "403"; }}@PreAuthorize("hasRole('user')") is used on the toAdmin() method, indicating that you need to have the user role to access this method. If you want to control the permissions level, you can use @PreAuthorize("hasPermission()"). This is just one of the usages. For more usage methods, you can read the official documentation. It should be noted that the default role prefix of Spring Security is "ROLE_", which has been added by default when using the hasRole method. Therefore, our user role in the database should be "ROLE_user", and the user prefix "ROLE_" is added before the user.
6. Test
Start the project and visit http://localhost:1130/login
Click to log in and enter "/hello"
Click to jump to the administrator page
In the background "/admin" url corresponding to the "user" method, users must have the "user" role. The user who logged in is also set in the database to have this role.
Now we modify the user role in the database and change it to "ROLE_admin". After logging out, log in again, click the "Go to Administrator Page" button again, and it will jump to the following page.
Because there is no "user" permission now, an exception was thrown during access, and it was intercepted and redirected to "/403".
7. POST access, error code 403
First, change "/admin" to POST request
@PreAuthorize("hasRole('user')") @RequestMapping(value = "/admin",method = RequestMethod.POST) public String toAdmin(){ return "helloAdmin"; }Change the request method of the "Go to Administrator Page" button from the original form expression get submission to ajax POST submission. As for why we are not submitted using form POST, we will talk about it later. Modify the code first
<body><h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1><!--<form th:action="@{/logout}" method="post"> <input type="submit" value="Sign Out"/></form><form th:action="@{/admin}" method="get"> <input th:type="submit" th:value="Go to the administrator page"/></form>--><a th:href="@{/admin}" rel="external nofollow" >Go to the administrator user page</a><input th:type="submit" onclick="testPost()" th:value="Go to the administrator page"/></body><script> function testPost() { $.ajax({ url:"/admin", type:'POST', success:function (data) { } }); }</script> Click the "Go to Administrator Page" button, and you can see the following in the debugging platform
This is because the framework prevents CSRF (Cross-site request forgery cross-site request forgery) from occurring, limiting most methods except get.
Here is a solution:
First add the following content in the tag.
<meta name="_csrf" th:content="${_csrf.token}"/> <meta name="_csrf_hader" th:content="${_csrf.headerName}"/> As long as this token is added, the background will verify the correctness of this token. If it is correct, it will accept post access.
Then add the following code in the ajax code:
var token = $('meta[name="_csrf"]').attr("content");var header = $('meta[name="_csrf_hader"]').attr("content");$(document).ajaxSend(function(e,xhr,opt){ xhr.setRequestHeader(header,token); }); In this way, you can access it normally using POST, DELETE and other methods.
The above mentioned using the POST method of form to submit. You can see by viewing the source code of the page.
The framework automatically inserts a hidden field into the form form, and the value value is that token, so using the form form form to submit a POST request can be directly passed, and if you submit it in ajax way, that code needs to be added.
Okay, that's all about this article. There will be articles later on how to use Spring Security to control permissions in the REST API style.
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.