(一)登录认证
说明:因为这里没有连接数据,我模拟两个用户,用户:zhang ,用户:liu,密码 123456 提前做了加密。
1.密码加密接口
/**
* 加密
* @param pwd
* @return
*/
@GetMapping("/register")
public SaResult register(String pwd) {
// 注册用户
String hashpw = BCrypt.hashpw(pwd);
return SaResult.data(hashpw);
}
2. 模拟数据库用户信息
/**
* 模拟数据库用户信息
*
* @param name
* @return
*/
private LoginUser builderLoginUser(String name) {
String dbPwd = "$2a$10$roWr2BV2LcMrGFo2ST.vluO4DkwLVt5LhozZGP4OKz3.eh31lAJ6q";
Set<String> perms = new HashSet<String>();
LoginUser loginUser = new LoginUser();
loginUser.setPwd(dbPwd);
loginUser.setName(name);
if ("zhang".equals(name)) {
perms.add("*.*.*");
loginUser.setId(10001);
loginUser.setMenuPermission(perms);
} else if ("liu".equals(name)){
perms.add("web:test:user");
loginUser.setId(10002);
loginUser.setMenuPermission(perms);
}
return loginUser;
}
3.登录接口
/**
* 登录
*
* @param name
* @param pwd
* @return
*/
@GetMapping("/doLogin")
public SaResult doLogin(String name, String pwd) {
LoginUser loginUser = builderLoginUser(name);
boolean checkpw = BCrypt.checkpw(pwd, loginUser.getPwd());
// 第一步:比对前端提交的账号名称、密码
if (checkpw) {
// 第二步:根据账号id,进行登录,指定设备类型
StpUtil.login(loginUser.getId(), DeviceType.PC.getDevice());
StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
return SaResult.data(StpUtil.getTokenInfo());
}
return SaResult.error("登录失败");
}
这里为了做简单测试就不做其它等校验。
StpUtil.login(用户ID,设备类型)
#test#result
登录成功之后尝试,用token访问下其他接口
登录认证成功。
(二)权限认证
所谓权限认证,核心逻辑就是判断一个账号是否拥有指定权限:
- 有,就让你通过。
- 没有?那么禁止访问!
深入到底层数据中,就是每个账号都会拥有一个权限码集合,框架来校验这个集合中是否包含指定的权限码。
例如:当前账号拥有权限码集合 ["user-add", "user-delete", "user-get"]
,这时候我来校验权限 "user-update"
,则其结果就是:验证失败,禁止访问。
1. 自定义SaPermissionImpl 实现StpInterface 接口
@Component
public class SaPermissionImpl implements StpInterface {
/**
* 获取菜单权限列表
*/
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
// 当用户调用权限验证的时候走
LoginUser loginUser = getLoginUser();
// 返回当前登录用户拥有的菜单集合
return new ArrayList<>(loginUser.getMenuPermission());
}
private LoginUser getLoginUser() {
return (LoginUser) StpUtil.getTokenSession().get(LOGIN_USER_KEY);
}
/**
* 获取角色权限列表
*/
@Override
public List<String> getRoleList(Object loginId, String loginType) {
return null;
}
}
说明:前面模拟两个用户,zhang ,权限集合["*.*.*"] 表示超级管理员,啥接口都能访问,liu 是普通用户,权限集合["web:test:user"]
新建两个测试接口:
@SaCheckPermission("web:test:user")
@GetMapping("/getLoginUser")
public SaResult getLoginUser() {
Object loginId = StpUtil.getLoginId();
System.out.println("接口[getLoginUser]当前用户:" + loginId);
return SaResult.data(loginId);
}
@SaCheckPermission("web:test:testPerms")
@GetMapping("/testPerms")
public SaResult testPerms() {
Object loginId = StpUtil.getLoginId();
System.out.println("接口[testPerms]当前用户:" + loginId);
return SaResult.data(loginId);
}
可以看到用户liu是没有第二个接口的权限的。
#test#result
用户zhang两个接口都这可以正常访问。
用户liu没有第二个接口的权限,测试结果成功。
### 这里输出的log其实是做了全局异常的捕获。
#GlobalExceptionHandler
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.util.SaResult;
import cn.hutool.http.HttpStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
/**
* @author yueF_L
* @version 1.0
* @date 2022-09-07 11:20
* 全局异常处理
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 认证失败
*/
@ExceptionHandler(NotLoginException.class)
public SaResult handleNotLoginException(NotLoginException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage());
return SaResult.get(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源", null);
}
/**
* 权限码异常
*/
@ExceptionHandler(NotPermissionException.class)
public SaResult handleNotPermissionException(NotPermissionException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage());
return SaResult.get(HttpStatus.HTTP_FORBIDDEN, "没有访问权限,请联系管理员授权",null);
}
}