1

问题:

如何在springboot项目中使用springsecurity去实现角色权限管理呢?本文将尽可能简单的一步步实现对接口的角色权限管理。

项目框架:

8vE37D.png

sql:

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;

maven:

在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>

model:

实体类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:

@Mapper  
public interface UserMapper{  
 User selectOneByName(User user);  
}

service:

public interface UserService{  
 User selectOneByName(User user) throws ServiceException;  
}

serviceImpl:

@Service  
public class UserServiceImpl implements UserService {  
 @Autowired  
 private UserMapper mapper;  
​  
 @Override  
 public User selectOneByName(User user) throws ServiceException {  
 return mapper.selectOneByName(user);  
 }  
}

mapper.xml:

<?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" propert="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>  

config:

首先实现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("删除失败");  
 }

怡蘅
3 声望1 粉丝

只要我开心就不会秃头:)