如何在springboot项目中使用springsecurity去实现角色权限管理呢?本文将尽可能简单的一步步实现对接口的角色权限管理。
项目框架:
user表:
CREATE TABLE `user` ( `Id` int NOT NULL AUTO_INCREMENT, `UserName` varchar(255) NOT NULL, `CreatedDT` datetime DEFAULT NULL, `Age` int DEFAULT NULL, `Gender` int DEFAULT NULL, `Password` varchar(255) NOT NULL, PRIMARY KEY (`Id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
role表:
CREATE TABLE `role` ( `Id` int NOT NULL AUTO_INCREMENT, `UserId` int DEFAULT NULL, `Role` varchar(255) DEFAULT NULL, `CreatedDT` datetime DEFAULT NULL, PRIMARY KEY (`Id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
在pom.xml中加入
<dependency/> <groupId/>org.projectlombok</groupId/> <artifactId/>lombok</artifactId/> <optional/>true</optional/> </dependency/> <!--SpringSecurity依赖配置--> <dependency/> <groupId/>org.springframework.boot</groupId/> <artifactId/>spring-boot-starter-security</artifactId/> </dependency/> <!--Hutool Java工具包--> <dependency/> <groupId/>cn.hutool</groupId/> <artifactId/>hutool-all</artifactId/> <version/>4.5.7</version/> </dependency/>
实体类User要实现springsecurity的基本接口UserDetails,UserDetails里继承了Serializable,不用担心序列化
@Data public class User implements UserDetails { public User() { } private static final long serialVersionUID = 1L; private Integer id; private String userName; private Date createdDT; private Integer age; private Integer gender; private String passWord; private String role; private List<GrantedAuthority> authorities; public User(String userName, String passWord, List<GrantedAuthority> authorities) { this.userName = userName; this.passWord = passWord; this.authorities = authorities; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } @Override public String getPassword() { return this.passWord; } @Override public String getUsername() { return this.userName; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
实体类role:
@Data public class Role implements Serializable { private Integer id; private String role; private Date createdDT; private Integer userId; }
@Mapper public interface UserMapper{ User selectOneByName(User user); }
public interface UserService extends BaseService<User/> { User selectOneByName(User user) throws ServiceException; }
@Service public class UserServiceImpl implements UserService { @Autowired private UserMapper mapper; @Override public User selectOneByName(User user) throws ServiceException { return mapper.selectOneByName(user); } }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace/="com.pzh.hyh.demo.mapper.UserMapper"/><!-- mapper相对路径--> <resultMap id/="BaseResultMap" type/="com.pzh.hyh.demo.model.User"/><!-- model相对路径--> <result column/="Id" jdbcType/="INTEGER" property/="id"/> <result column/="UserName" jdbcType/="VARCHAR" property/="userName"/> <result column/="CreatedDT" jdbcType/="TIMESTAMP" property/="createdDT"/> <result column/="Age" jdbcType/="INTEGER" property/="age"/> <result column/="Gender" jdbcType/="INTEGER" property/="gender"/> <result column/="Password" jdbcType/="VARCHAR" property/="passWord"/> </resultMap/> <sql id/="Base/_Column/_List"/> Id, UserName, CreatedDT, Age, Gender,Password </sql/> <select id/="selectOneByName" parameterType/="com.pzh.hyh.demo.model.User" resultMap/="BaseResultMap"/><!-- model相对路径--> SELECT u./*,r.role FROM /`user/` u LEFT JOIN role r on u.Id = r.UserId where u.UserName = #{userName,jdbcType=VARCHAR} </select/> </mapper/>
首先实现UserDetailsService类。自定义获取用户信息和角色信息。
@Component public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserService userService; @Autowired private HttpServletRequest request; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 通过用户名从数据库获取用户信息 User user /= userService.selectOneByName(new User(){ { setUserName(username); } }); if (user /== null) { throw new UsernameNotFoundException("用户不存在"); } HttpSession session /= request.getSession(); session.setAttribute(session.getId(),user); // 得到用户角色 String role /= user.getRole(); // 角色集合 List<GrantedAuthority/> authorities /= new ArrayList<>(); // 角色必须以/`ROLE/_/`开头,数据库中没有,则在这里加 authorities.add(new SimpleGrantedAuthority("ROLE/_" + role)); return new User( user.getUsername(), user.getPassword(), authorities ); } }
自定义错误提示
@Component public class MyAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { response.setCharacterEncoding("UTF-8"); response.setContentType("application/json"); response.getWriter().println("{'code':'403','message':'没有访问权限'}"); response.getWriter().flush(); } }
终于来到security的配置了
@EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled /= true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private CustomUserDetailsService userDatailService; @Autowired private MyAccessDeniedHandler accessDeniedHandler; @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(userDatailService) .passwordEncoder(passwordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http .headers().frameOptions().disable() .and() .authorizeRequests() .antMatchers("不限制访问的路径,如:'/user//*'").permitAll() .antMatchers("用户拥有规定角色才允许访问的路径,如:'/user/delte'").hasRole("admin") .antMatchers("规定ip才允许访问的路径,如:'//*'").hasIpAddress("192.168.1.1/24"); .anyRequest().authenticated() // 所有请求都需要验证 .and() // 跳转自定义成功页 .formLogin().defaultSuccessUrl("/html/index.html") .and() .exceptionHandling() //用户无权限访问链接,给出友好提示 .accessDeniedHandler(accessDeniedHandler) .and() .csrf().disable();// post请求要关闭csrf验证,不然访问报错;实际开发中要开启。 } }
至此,springsecurity的角色权限管理就完成了,如果想要实现方法级的角色权限限制,可以在方法前加入 @PreAuthorize("hasRole('角色')")注解,多个角色可以使用hasAnyRole(),就可以限制拥有规定角色权限的用户才能访问了。
@PreAuthorize("hasRole('admin')") @RequestMapping(value /= "/delete") public CommonResult delete(@RequestBody int id) { int i /= userService.delete(new User() { { setId(id); } }); return i /> 0 ? processSuccess("删除成功") : processFailure("删除失败"); }