前言:
存在着一部分后台管理系统,需要对用户进行角色划分,然后根据角色对应的权限来限制访问的内容;又或者是一些门户网站,只有特殊的人员才能看到对应的页面信息。这些都需要权限控制。
技术点:
- Spring Boot+Spring Security
工程依赖:
org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot-starter-web
代码:
实体类:
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;/** *系统用户实体类.
* * @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;}
配置类:
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;/** *Spring-Security配置类.
* * @author menghao. * @version 2018/3/22. */@Configurationpublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final DataSource dataSource; // JPA仓库(代码略) private final UserRepository userRepository; @Autowired public WebSecurityConfig(@Qualifier("dataSource") DataSource dataSource, @Qualifier("userRepository") UserRepository userRepository) { this.dataSource = dataSource; this.userRepository = userRepository; } /** * 定义用户认证逻辑 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 三选一 // memoryMode(auth); // jdbcMode(auth); customizeMode(auth); } /** * 定义用户授权逻辑 */ @Override protected void configure(HttpSecurity http) throws Exception { // 定义登录行为:允许任意访问,登录成功、失败页地址 http.formLogin().loginPage("/login").defaultSuccessUrl("/auth/index").failureUrl("/login").permitAll() .and() // 定义Cookie及存在时间 .rememberMe().tokenValiditySeconds(-1).key("marvel") .and() // 定义角色MANAGER访问路径 .authorizeRequests().antMatchers("/manager/**").hasRole("MANAGER") .and() // 定义角色USER访问路径 .authorizeRequests().antMatchers("/user/**").hasRole("USER") .and() // 定义公共访问路径 .authorizeRequests().antMatchers("/common/**").hasAnyRole("USER", "MANAGER") .and() // 定义注销行为 .logout().logoutUrl("/logout").logoutSuccessUrl("/login").permitAll(); } /** * 内存模式 * inMemoryAuthentication:在内存中添加用户并指定权限 */ private void memoryMode(AuthenticationManagerBuilder auth) throws Exception { // Spring Security5之后需要指定,否则会抛异常 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模式 * jdbcAuthentication:默认的查询在JdbcDaoImpl中实现 * * @see UserDetails */ private void jdbcMode(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication().dataSource(dataSource) // Spring Security5之后需要指定,否则会抛异常 .passwordEncoder(new BCryptPasswordEncoder()) // 需要查询出三个字段:用户名、密码、enable .usersByUsernameQuery(QUERY_BY_USERNAME) // 需要查询出两个字段:用户名、权限 .authoritiesByUsernameQuery(QUERY_AUTHORITIES_BY_USERNAME); } @Bean public UserDetailsService customizeUserDetailsService() { // 实现:loadUserByUsername(String username),返回一个UserDetails类型对象 return username -> { SystemUser systemUser = userRepository.findByUsername(username); Listauthorities = new ArrayList<>(); authorities.add(new SimpleGrantedAuthority(systemUser.getRole())); return new User(systemUser.getUsername(), systemUser.getPassword(), authorities); }; } /** * 自定义模式 * userDetailsService:指定一个返回UserDetails类型的实现 */ 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;/** *公共Controller.
* * @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;/** *管理员Controller.
* * @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;/** *普通用户Controller.
* * @author menghao. * @version 2018/3/24. */@RestController@RequestMapping("user")public class UserController { @RequestMapping("data") public String managePage() { return "you are an user"; }}
登录页面:
登录页 Login Form
结果:
当角色USER的用户登录后,能访问http://localhost/user/data和http://localhost/common/data,并展示对应的字符串;访问http://localhost/manager/data,会返回403错误页面。
角色MANAGER的用户情况相反。
总结:
Spring Boot和Spring Security集成是十分容易的事情,如果需要页面更细粒度的权限控制,需要引入
org.thymeleaf.extras thymeleaf-extras-springsecurity4 2.1.2.RELEASE
这样就可以使用类似 sec:authorize=”hasRole(‘ROLE_USER’) 这样来控制页面元素的展示。