《Spring Boot 实战派》--14.开发企业级通用的后台系统
第14章 开发企业级通用的后台系统
使用Spring Boot,免不了开发后台系统。所以,本章通过实现一个基于角色的访问控制后台 系统,来系统地介绍如何使用Spring Security。
本实例的源代码可以在“/14/ManagementSystemDemo”目录下找到。
14.1用JPA实现实体间的映射关系
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 Listroles; public long getld() { return id; }
/**
* 根据自定义逻辑来返回用户权限。如果用户权限返回空,或者和拦截路径对应权限不同,则验证不通过 */ @Override public Collection<? extends GrantedAuthority> getAuthorities() { Listauthorities = 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 Listpermissions;
/** * 用户一角色关系:多对多关系 */ @ManyToMany @JoinTable(name = "SysUserRole", joinColumns = (@JoinColumn(name = "roleld")), inverseJoinColumns = (@JoinColumn(name = "uid"))) /** * —个角色对应多个用户 */ private Listuserinfos; )
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 Listroles;
public List getPermissionsO ( return Arrays.asList(this.permission.trimO.split('T')); }
public void setPermissions(List permissions) { this.permissions = permissions;
}
}
14.2用Spring 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) { ListsysRole = sysRoleRepository.findAII(); model.addAttribute("sysRole", sysRole); return "admin/permission/add";
}
@PostMapping("/add") public String addPermission(SysPermission sysPermission, String role) ( Listroles = new ArrayList<>(); SysRole rolel = sysRoleRepository.findByRole(role); roles.add(rolel); sysPermission.setRoles(roles); sysPermissionRepository.save(sysPermission); return "redirect:/admin/"; } }
2.实现视图模板
在以下代码中,视图中的$(sysRole)是根据控制器返回的参数;“th:each”标签是Thymeleaf 的标签,用于遍历数据。
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) ( Listadminrole = 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); Listroles = new ArrayList<>(); SysRole rolel = sysRoleRepository.findByRole(role); roles.add(rolel); user,setRoles(roles); adminUserRepository.save(user); return "redirect:/admin/"; }
2.视图页面
在视图页面中请注意遍历ll${adminrole}"这个用户角色,见以下代码: