《Spring Boot 实战派》--14.开发企业级通用的后台系统


第14章 开发企业级通用的后台系统

使用Spring Boot,免不了开发后台系统。所以,本章通过实现一个基于角色的访问控制后台 系统,来系统地介绍如何使用Spring Security。

本实例的源代码可以在“/14/ManagementSystemDemo”目录下找到。

14.1JPA实现实体间的映射关系

RBAC Role Based Access Control)是基于角色的访问控制,一般分为用户user)、 (role)、权限(permission 3个实体。它们的关系如下:

  • 角色(role)和权限(permission )是多对多关系。
  • 用户(user)和角色(role)也是多对多的关系。
  • 用户(user)和权限(permission )之间没有直接的关系。用户需要通过角色作为代理(中 间人)来获取到拥有的权限。

5 张表就能实现角色、用户、权限的映射关系,其中包含3个实体表和2个关系表(角色一权 限关系表、用户一角色关系表)。

14.1.1创建用户实体

用户实体类通过实现UserDetaiis接口实现认证及授权,见以下代码:

package com.example.demo.entity.sysuser;

?Entity 
public class SysUser implements UserDetails (

  //主键及自动增长   @ld   @GeneratedValue   private long id;
  @Column(nullable = false, unique = true)   private String name;   private String password;   private String cnname;   private Boolean enabled = Boolean.TRUE;   /**    * 多对多映用户角色    */   @ManyToMany(cascade = (CascadeType.REFRESH), fetch = FetchType.EAGER)
  private List roles;   public long getld() {     return id;   }
  /**
   * 根据自定义逻辑来返回用户权限。如果用户权限返回空,或者和拦截路径对应权限不同,则验证不通过    */   @Override   public Collection<? extends GrantedAuthority> getAuthorities() {     List authorities = new ArrayList<>();     List roles = this.getRoles();     for (SysRole role : roles) {
      authorities.add(new SimpleGrantedAuthority(role.getRole()));     }     return authorities;   } }

14.1.2创建角色实体

角色是用户和权限的中间代理表。用户(user)和权限(permission )之间没有直接的关系, 用户(user)需要通过角色作为代理(中间人)来获取拥有的权限,见以下代码:

@Data
@Entity
public class SysRole {
  @ld
  @GeneratedValue
  /**
   * 编号
   */
  private Integer id;
  private String cnname;
  /**
   * 角色标识,如“管理员”
  */
  private String role;
  /**    * 角色描述,用于在Ul界面显示角色信息    */   private String description;
  /**    * 是否可用。如果不可用,则不会添加给用户    */   private Boolean available = Boolean.FALSE;
  /**    * 角色一权限关系:多对多关系    */   @ManyToMany(fetch = FetchType. EAGER)   @JoinTable(name = "SysRolePermission", joinColumns = (@JoinColumn(name ="roleld")), inverseJoinColumns = {@JoinColumn(name = "permissionld")))   private List permissions;
  /**    * 用户一角色关系:多对多关系    */   @ManyToMany   @JoinTable(name = "SysUserRole", joinColumns = (@JoinColumn(name = "roleld")), inverseJoinColumns = (@JoinColumn(name = "uid")))   /**    * —个角色对应多个用户    */   private List userinfos; )

14.1.3创建权限实体

权限和角色存在多对多关系。一般情况下,权限不会和用户直接关联,它用于存放权限信息, 比如权限的名称、HTTP方法、URL路径。具体见以下代码:

@Data
?Entity
public class SysPermission implements Serializable {

  @ld   @GeneratedValue   /**    * 主键   */   private Integer id;   /**    * 名称    */   private String name;
  @Column(columnDefinition = "enumCmenu','button')")   /**    * 资源类型,[menu|button]    */   private String resourceType;
  /**    * 资源路径    */   private String url;
  /**      * 权限字符串。menu
   * 例子:role:*; button 例子:role:create,role:update,role:delete,role:view    */   private String permission;
  /**   *父编号   */   private Long parentld;
  /**    * 父编号列表    */   private String parentlds;
  private Boolean available = Boolean.FALSE;
  ?Transient   private List permissions;
  @ManyToMany   @JoinTable(name = "SysRolePermission", joinColumns = {@JoinColumn(name = "permissionld")), inverseJoinColumns = {@JoinColumn(name = "roleld")))   private List roles;
  public List getPermissionsO (     return Arrays.asList(this.permission.trimO.split('T'));   }
  public void setPermissions(List permissions) {     this.permissions = permissions;
  }
}

14.2Spring Security实现动态授权RBAC)功能

14.2.1实现管理(增加、删除、修改和查询)管理员角色功能

1、实现控制器

控制器主要指定URL映射和视图,见以下代码:

?Controller
@RequestMapping("admin")
public class SysRoleControlller {

  @Autowired   private SysRoleRepository sysRoleRepository;   

  @RequestMapping("/role/add")   public String addRole() {     return "admin/role/add";   }
     @RequestMapping("/role")   public String addRole(SysRole model) {     String role = "ROLE_" + model.getRole();     model.setRole(role);     sysRoleRepository.save(model);     return "redirect:/admin/";   } )

2、视图页面

这里注意,要提交CSRF的token (根据需求可以不幵启CSFR), token的值需要在HTML 中的head标签中添加。见下面代码注释标签之间的部分,以及在表单(form ) 中的隐臓提交CSRF的token值。





〈input type="submit" value=”提交"class="btn btn-info" />     

14.2.2实现管理权限功能

1.实现权限控制器

权限管理主要是对权限迸行增加、删除' 修改和查询操作。权限要和角色对应起来。在进行操 作时需要附带角色字段,见以下代码:

?Controller
@RequestMapping(7admin/permission")
public class SysPermissionControler {
  @Autowired   private SysPermissionRepository sysPermissionRepository;
  @Autowired   private SysRoleRepository sysRoleRepository;
  @RequestMapping("/add")   public String addPermission(Model model) {     List sysRole = sysRoleRepository.findAII();     model.addAttribute("sysRole", sysRole);     return "admin/permission/add";
  }
  @PostMapping("/add")   public String addPermission(SysPermission sysPermission, String role) (     List roles = new ArrayList<>();     SysRole rolel = sysRoleRepository.findByRole(role);     roles.add(rolel);     sysPermission.setRoles(roles);     sysPermissionRepository.save(sysPermission);     return "redirect:/admin/";   } }

2.实现视图模板

在以下代码中,视图中的$(sysRole)是根据控制器返回的参数;“th:each”标签是Thymeleaf 的标签,用于遍历数据。

〈input type二"submit" value="提交"class="btn btn-info'7>      

14.2.3实现管理管理员功能

管理员密码是需要加密的,这里采用“BCrypt”方式加密。如果读者用自己喜欢的加密方式, 则需要新建加密工具类,同时要在安全配置类重写加密配置方式。

1.实现控制器

主要注意密码加密■'BCryptPasswordEncoder"和角色遍历部分,见以下代码:

@Autowired
private SysUserRepository adminUserRepository;

@Autowired private SysRoleRepository sysRoleRepository;
//@PreAuthorize("hasRole('ROLE_admin')") @RequestMapping(7user/add") public String toAddUser(Model model) (   List adminrole = sysRoleRepository.findAII();   model.addAttribute("adminrole", adminrole);   return "admin/user/add"; }
//@RequestMapping('7user/add") @PostMapping(*7user") public String addUser(String name, String password, String role) {   BCryptPasswordEncoder encoder = new BCryptPasswordEncoderO;   String encodePassword 二 encoder.encode(password);   SysUser user = new SysUser(name, encodePassword);   List roles = new ArrayList<>();   SysRole rolel = sysRoleRepository.findByRole(role);   roles.add(rolel);   user,setRoles(roles);   adminUserRepository.save(user);   return "redirect:/admin/"; }

2.视图页面

在视图页面中请注意遍历ll${adminrole}"这个用户角色,见以下代码:

用户名 〈input type="text" name="name" id二"name” placeholder="name7> 〈input type二"password" name="password" id="password" placeholde「="passwo「cT/>