RBAC模型(Role-Based Access Control:基于角色的访问控制)是早期要提出的基于用户而实现权限控制的一个理论,目前大部分的框架也都是基于RBAC的理论去实现权限的控制;
上篇文章我们使用springsecurity 实现了我们web项目的登录认证和权限管理,本篇文章我们就自己去大家一个简单的权限管理的RBAC项目;
首先我们需要知道实现权限管理,我们大致需要哪些类;
1.用户实体类(里边添加用户名,以及用户密码);
2.角色实体类(里边添加,角色id和角色名 字段);
3.资源实体类(里边有资源id,资源类型,资源名称字段);
然后我们需要理清实体类之间的关系;首先,一个用户会拥有自己的角色,比如说 王强 用户在公司任职 CEO CEO就是一个角色;然后每个用户下边有自己的工作经理,任职部门,出生年月,姓名等等,这些成为该用户的资源info,当然有的用户角色不止一个,所以该用户的资源根据角色的不同也会不相同,因此角色下边也会有资源;
这样我们就可以设计我们的实体类了;
资源实体类下边是不包含其他的类的,所以我们先设计资源实体类:
ResourcesBean 资源实体类:
//资源实体类
public class ResourcesBean {
private String resourceId;
private String resourceType;
private String resourceName;
//下边写set get construtor 方法;
接下来是roleBean 角色实体类:但是我们的角色下边是有资源的,因此要与资源实体类有关联,这边我们使用list集合把资源注入到我们的角色实体类当中:
/*
* 角色实体类
* */
public class RoleBean {
private String roleId;
private String roleName;
private List<ResourcesBean> resources;//给角色注入资源(userinfo);
//下边为set get construtor 方法
接下来就是我们的用户实体类 UserBean: 用户实体类既要与我们的角色实体类关联,也要与我们的资源实体类关联;同样我们也使用 list集合注入 rolelist 和 resourceslist;
/*
* 用户实体类
* */
public class UserBean {
private String userId;
private String userName;
private String userPass;
private List<RoleBean> userRoles = new ArrayList<>();
private List<ResourcesBean> resourceBeans = new ArrayList<>();
这样我们的一个权限认证的实体类就设计完成了
接下来我们需要给我们的实体类添加上数据,并加上关系(项目之中都是需要连接数据库去查询 用户名,账号,角色,资源),在这里我们直接赋值来模拟数据库查询;
创建TestData :
package com.example.demo.entity;
import com.example.demo.bean.ResourcesBean;
import com.example.demo.bean.RoleBean;
import com.example.demo.bean.UserBean;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class TestData {
private List<UserBean> allUser;
/**
* 模拟数据库获取到的数据。
* admin用户 拥有admin角色,拥有mobile和salary两个资源。
* mobile用户,拥有mobile角色,拥有mobile资源。
* worker用户,拥有worker角色,没有资源。
* @return
*/
private List<UserBean> getAllUser(){
// 如果该用户不为空,
if (null ==allUser){
allUser =new ArrayList<>();
//资源添加mobile 和salary 资源
ResourcesBean mobileResource=new ResourcesBean("1","mobile");
ResourcesBean salaryResource=new ResourcesBean("2","salary");
List<ResourcesBean> adminResource=new ArrayList<>();
// 将mobile salary资源存入到我们的资源集合adminResource当中
adminResource.add(salaryResource);
adminResource.add(mobileResource);
// 只将mobile资源存入到我们的资源集合 managerResources
List<ResourcesBean> managerResources = new ArrayList<>();
managerResources.add(mobileResource);
// admin 有mobile 角色 并且拥有上边 adminResource的两个资源
RoleBean adminRole = new RoleBean("1","admin");
adminRole.setResources(adminResource);
// mobile用户具有 mobile角色,并且具有mobile资源
RoleBean managerRole = new RoleBean("2","mobile");
managerRole.setResources(managerResources);
//work具有work
RoleBean workRole = new RoleBean("3","w");
List<RoleBean> adminRoles=new ArrayList<>();
adminRoles.add(adminRole);
List<RoleBean> managerRoles=new ArrayList<>();
managerRoles.add(managerRole);
List<RoleBean> workRoles=new ArrayList<>();
workRoles.add(workRole);
UserBean admin = new UserBean("1","admin","admin");
admin.setUserRoles(adminRoles);
admin.setResourceBeans(adminResource);
UserBean mobile = new UserBean("2","manager","manager");
mobile.setUserRoles(managerRoles);
mobile.setResourceBeans(managerResources);
UserBean work = new UserBean("3","worker","worker");
work.setUserRoles(workRoles);
allUser.add(admin);
allUser.add(mobile);
allUser.add(work);
}
return allUser;
}
/*
* 判断用户的账号和密码是否正确
* */
public UserBean qeryUser(UserBean user){
List<UserBean> allUser = this.getAllUser();
// 将用户集合遍历 判断登录是否正确
List<UserBean> userList = allUser.stream().filter(userBean ->
userBean.getUserName().equals(user.getUserPass())
&&
userBean.getUserPass().equals(user.getUserPass())
).collect(Collectors.toList());
return userList.size()>0?userList.get(0):null;
}
}
接下来就是创建我们的拦截器,所有访问的接口都要跳转到 登录页面 进行登录,获取会话session;
首先先创建我们的路由枚举常量,方便后边的代码编写;
package com.example.demo.constants;
public class MyConstants {
// 设置 枚举常量
public static final String FLAG_CURRENTUSER = "currentUser";
public static final String RESOURCE_COMMON = "common";
public static final String RESOURCE_MOBILE = "mobile";
public static final String RESOURCE_SALARY = "salary";
}
拦截器配置:
package com.example.demo.config;
import com.example.demo.bean.UserBean;
import com.example.demo.constants.MyConstants;
import com.sun.xml.internal.ws.handler.HandlerException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class AuthInterceptor implements HandlerInterceptor {
//授权拦截
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 不需要登录 就可以访问的路径
String requestURI =request.getRequestURI();
if (requestURI.contains(".")|| requestURI.startsWith("/"+ MyConstants.RESOURCE_COMMON+"/")){
return true;
}
// 未登录用户,直接拒绝访问
// 获取用户的属性 并进行判断是否为空
if ((null == request.getSession().getAttribute(MyConstants.FLAG_CURRENTUSER))) {
//如果不为空设置response 的返回使用utf-8的编码样式
response.setCharacterEncoding("UTF-8");
/*
* 返回打印字符串 please login first
* */
response.getWriter().write("please login first");
return false;
}else {
UserBean currentUser =(UserBean) request.getSession().getAttribute(MyConstants.FLAG_CURRENTUSER);
// 已登录用户,判断是否又资源访问权限
// 如果用户的请求地址是mobile,而且用户又该接口的访问权限
if (requestURI.startsWith("/"+MyConstants.RESOURCE_MOBILE+"/")&¤tUser.havaPermission(MyConstants.RESOURCE_MOBILE)){
return true;
// 如果用户访问的是salary接口 并且该用户具有权限
}else if (requestURI.startsWith("/"+MyConstants.RESOURCE_SALARY+"/")&& currentUser.havaPermission(MyConstants.RESOURCE_SALARY)){
return true;
}else {
// 如果用户不具备权限 或者请求地址不对 就返回打印出 no auth to visit;
response.setCharacterEncoding("UTF-8");
response.getWriter().write("no auth to visit");
return false;
}
}
}
}
package com.example.demo.config;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
/*
* 配置拦截器和页面跳转
*
* */
@Component
public class MyWebAppConfigurer implements WebMvcConfigurer {
@Resource
private AuthInterceptor authInterceptor;
//配置权限拦截:拦截所有请求
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor).addPathPatterns("/**");
}
//简单配置启动页面 所有页面首先跳转至我们的登录页面
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("redirect:/index.html");
}
}
配置完拦截器,我们需要配置一下我们的登录接口:
package com.example.demo.controller;
import com.example.demo.bean.UserBean;
import com.example.demo.constants.MyConstants;
import com.example.demo.service.AuthService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.logging.Logger;
/*
* 登录页面配置
* */
@RestController
@RequestMapping("/common")
public class loginController {
/*
* 因为是登陆页面 我们设置本类的日志 ->可以向控制台打印日志
*
* */
private final Logger logger= (Logger) LoggerFactory.getLogger(this.getClass());
@Resource
private AuthService authService;
@PostMapping("/login")
public UserBean login(UserBean loginUser, HttpServletRequest request){
UserBean user = authService.userLogin(loginUser);
if(null != user){
logger.info("user login succeed");
request.getSession().setAttribute(MyConstants.FLAG_CURRENTUSER,user);
}
logger.info("user login failed");
return user;
}
// 获取当前用户
@PostMapping("/getCurrentUser")
public Object getCurrentUser(HttpSession session){
return session.getAttribute(MyConstants.FLAG_CURRENTUSER);
}
@PostMapping("/logout")
// 注销
public void logout(HttpSession session){
session.removeAttribute(MyConstants.FLAG_CURRENTUSER);
}
}
接下来是我们的资源接口 mobile 和salary接口;
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/mobile")
public class MobileController {
@GetMapping ("/query")
public String query(){
return "mobile";
}
}
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/salary")
public class SalaryController {
@GetMapping("/query")
public String query(){
return "salary";
}
}
这样呢简单的一套后端RBAC项目后端就编写完成了;我们登录不同的用户会有不同的资源权限!