目录
JWT
JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。
我们利用一定的编码生成 Token,并在 Token 中加入一些非敏感信息,将其传递。
一个完整的 Token : eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM
在本项目中,我们规定每次请求时,需要在请求头中带上 token ,通过 token 检验权限,如没有,则说明当前为游客状态(或者是登陆 login 接口等)
JWTUtil
我们利用 JWT 的工具类来生成我们的 token,这个工具类主要有生成 token 和 校验 token 两个方法
生成 token 时,指定 token 过期时间 EXPIRE_TIME
和签名密钥 SECRET
,然后将 date 和 username 写入 token 中,并使用带有密钥的 HS256 签名算法进行签名
/**
* Created with IntelliJ IDEA
*
* @Author yuanhaoyue swithaoy@gmail.com
* @Description JWT 工具类
* @Date 2018-04-07
* @Time 22:48
*/
public class JwtUtil {
// 过期时间 24 小时
private static final long EXPIRE_TIME = 60 * 24 * 60 * 1000;
// 密钥
private static final String SECRET = "%8qwunung1234%$#^#R@sdf";
/**
* 生成 token, 5min后过期
*
* @param username 用户名
* @return 加密的token
*/
public static String createToken(String username) {
try {
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(SECRET);
// 附带username信息
return JWT.create()
.withClaim("username", username)
//到期时间
.withExpiresAt(date)
//创建一个新的JWT,并使用给定的算法进行标记
.sign(algorithm);
} catch (UnsupportedEncodingException e) {
return null;
}
}
/**
* 校验 token 是否正确
*
* @param token 密钥
* @param username 用户名
* @return 是否正确
*/
public static boolean verify(String token, String username) {
try {
Algorithm algorithm = Algorithm.HMAC256(SECRET);
//在token中附带了username信息
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("username", username)
.build();
//验证 token
verifier.verify(token);
return true;
} catch (Exception exception) {
return false;
}
}
/**
* 获得token中的信息,无需secret解密也能获得
*
* @return token中包含的用户名
*/
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
return null;
}
}
}
数据库表
sql
/*
Navicat MySQL Data Transfer
Source Server : pro-markerhub
Source Server Version : 50727
Source Host : 129.204.23.53:3306
Source Database : vueadmin
Target Server Type : MYSQL
Target Server Version : 50727
File Encoding : 65001
Date: 2021-01-30 21:02:31
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for sys_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_menu`;
CREATE TABLE `sys_menu` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`parent_id` bigint(20) DEFAULT NULL COMMENT '父菜单ID,一级菜单为0',
`name` varchar(64) NOT NULL,
`path` varchar(255) DEFAULT NULL COMMENT '菜单URL',
`perms` varchar(255) DEFAULT NULL COMMENT '授权(多个用逗号分隔,如:user:list,user:create)',
`component` varchar(255) DEFAULT NULL,
`type` int(5) NOT NULL COMMENT '类型 0:目录 1:菜单 2:按钮',
`icon` varchar(32) DEFAULT NULL COMMENT '菜单图标',
`orderNum` int(11) DEFAULT NULL COMMENT '排序',
`created` datetime NOT NULL,
`updated` datetime DEFAULT NULL,
`statu` int(5) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_menu
-- ----------------------------
INSERT INTO `sys_menu` VALUES ('1', '0', '系统管理', '', 'sys:manage', '', '0', 'el-icon-s-operation', '1', '2021-01-15 18:58:18', '2021-01-15 18:58:20', '1');
INSERT INTO `sys_menu` VALUES ('2', '1', '用户管理', '/sys/users', 'sys:user:list', 'sys/User', '1', 'el-icon-s-custom', '1', '2021-01-15 19:03:45', '2021-01-15 19:03:48', '1');
INSERT INTO `sys_menu` VALUES ('3', '1', '角色管理', '/sys/roles', 'sys:role:list', 'sys/Role', '1', 'el-icon-rank', '2', '2021-01-15 19:03:45', '2021-01-15 19:03:48', '1');
INSERT INTO `sys_menu` VALUES ('4', '1', '菜单管理', '/sys/menus', 'sys:menu:list', 'sys/Menu', '1', 'el-icon-menu', '3', '2021-01-15 19:03:45', '2021-01-15 19:03:48', '1');
INSERT INTO `sys_menu` VALUES ('5', '0', '系统工具', '', 'sys:tools', null, '0', 'el-icon-s-tools', '2', '2021-01-15 19:06:11', null, '1');
INSERT INTO `sys_menu` VALUES ('6', '5', '数字字典', '/sys/dicts', 'sys:dict:list', 'sys/Dict', '1', 'el-icon-s-order', '1', '2021-01-15 19:07:18', '2021-01-18 16:32:13', '1');
INSERT INTO `sys_menu` VALUES ('7', '3', '添加角色', '', 'sys:role:save', '', '2', '', '1', '2021-01-15 23:02:25', '2021-01-17 21:53:14', '0');
INSERT INTO `sys_menu` VALUES ('9', '2', '添加用户', null, 'sys:user:save', null, '2', null, '1', '2021-01-17 21:48:32', null, '1');
INSERT INTO `sys_menu` VALUES ('10', '2', '修改用户', null, 'sys:user:update', null, '2', null, '2', '2021-01-17 21:49:03', '2021-01-17 21:53:04', '1');
INSERT INTO `sys_menu` VALUES ('11', '2', '删除用户', null, 'sys:user:delete', null, '2', null, '3', '2021-01-17 21:49:21', null, '1');
INSERT INTO `sys_menu` VALUES ('12', '2', '分配角色', null, 'sys:user:role', null, '2', null, '4', '2021-01-17 21:49:58', null, '1');
INSERT INTO `sys_menu` VALUES ('13', '2', '重置密码', null, 'sys:user:repass', null, '2', null, '5', '2021-01-17 21:50:36', null, '1');
INSERT INTO `sys_menu` VALUES ('14', '3', '修改角色', null, 'sys:role:update', null, '2', null, '2', '2021-01-17 21:51:14', null, '1');
INSERT INTO `sys_menu` VALUES ('15', '3', '删除角色', null, 'sys:role:delete', null, '2', null, '3', '2021-01-17 21:51:39', null, '1');
INSERT INTO `sys_menu` VALUES ('16', '3', '分配权限', null, 'sys:role:perm', null, '2', null, '5', '2021-01-17 21:52:02', null, '1');
INSERT INTO `sys_menu` VALUES ('17', '4', '添加菜单', null, 'sys:menu:save', null, '2', null, '1', '2021-01-17 21:53:53', '2021-01-17 21:55:28', '1');
INSERT INTO `sys_menu` VALUES ('18', '4', '修改菜单', null, 'sys:menu:update', null, '2', null, '2', '2021-01-17 21:56:12', null, '1');
INSERT INTO `sys_menu` VALUES ('19', '4', '删除菜单', null, 'sys:menu:delete', null, '2', null, '3', '2021-01-17 21:56:36', null, '1');
-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(64) NOT NULL,
`code` varchar(64) NOT NULL,
`remark` varchar(64) DEFAULT NULL COMMENT '备注',
`created` datetime DEFAULT NULL,
`updated` datetime DEFAULT NULL,
`statu` int(5) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`) USING BTREE,
UNIQUE KEY `code` (`code`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('3', '普通用户', 'normal', '只有基本查看功能', '2021-01-04 10:09:14', '2021-01-30 08:19:52', '1');
INSERT INTO `sys_role` VALUES ('6', '超级管理员', 'admin', '系统默认最高权限,不可以编辑和任意修改', '2021-01-16 13:29:03', '2021-01-17 15:50:45', '1');
-- ----------------------------
-- Table structure for sys_role_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_menu`;
CREATE TABLE `sys_role_menu` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`role_id` bigint(20) NOT NULL,
`menu_id` bigint(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=102 DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of sys_role_menu
-- ----------------------------
INSERT INTO `sys_role_menu` VALUES ('60', '6', '1');
INSERT INTO `sys_role_menu` VALUES ('61', '6', '2');
INSERT INTO `sys_role_menu` VALUES ('62', '6', '9');
INSERT INTO `sys_role_menu` VALUES ('63', '6', '10');
INSERT INTO `sys_role_menu` VALUES ('64', '6', '11');
INSERT INTO `sys_role_menu` VALUES ('65', '6', '12');
INSERT INTO `sys_role_menu` VALUES ('66', '6', '13');
INSERT INTO `sys_role_menu` VALUES ('67', '6', '3');
INSERT INTO `sys_role_menu` VALUES ('68', '6', '7');
INSERT INTO `sys_role_menu` VALUES ('69', '6', '14');
INSERT INTO `sys_role_menu` VALUES ('70', '6', '15');
INSERT INTO `sys_role_menu` VALUES ('71', '6', '16');
INSERT INTO `sys_role_menu` VALUES ('72', '6', '4');
INSERT INTO `sys_role_menu` VALUES ('73', '6', '17');
INSERT INTO `sys_role_menu` VALUES ('74', '6', '18');
INSERT INTO `sys_role_menu` VALUES ('75', '6', '19');
INSERT INTO `sys_role_menu` VALUES ('76', '6', '5');
INSERT INTO `sys_role_menu` VALUES ('77', '6', '6');
INSERT INTO `sys_role_menu` VALUES ('96', '3', '1');
INSERT INTO `sys_role_menu` VALUES ('97', '3', '2');
INSERT INTO `sys_role_menu` VALUES ('98', '3', '3');
INSERT INTO `sys_role_menu` VALUES ('99', '3', '4');
INSERT INTO `sys_role_menu` VALUES ('100', '3', '5');
INSERT INTO `sys_role_menu` VALUES ('101', '3', '6');
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(64) DEFAULT NULL,
`password` varchar(64) DEFAULT NULL,
`avatar` varchar(255) DEFAULT NULL,
`email` varchar(64) DEFAULT NULL,
`city` varchar(64) DEFAULT NULL,
`created` datetime DEFAULT NULL,
`updated` datetime DEFAULT NULL,
`last_login` datetime DEFAULT NULL,
`statu` int(5) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UK_USERNAME` (`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', 'admin', '$2a$10$R7zegeWzOXPw871CmNuJ6upC0v8D373GuLuTw8jn6NET4BkPRZfgK', 'https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg', '123@qq.com', '广州', '2021-01-12 22:13:53', '2021-01-16 16:57:32', '2020-12-30 08:38:37', '1');
INSERT INTO `sys_user` VALUES ('2', 'test', '$2a$10$0ilP4ZD1kLugYwLCs4pmb.ZT9cFqzOZTNaMiHxrBnVIQUGUwEvBIO', 'https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg', 'test@qq.com', null, '2021-01-30 08:20:22', '2021-01-30 08:55:57', null, '1');
-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL,
`role_id` bigint(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('4', '1', '6');
INSERT INTO `sys_user_role` VALUES ('7', '1', '3');
INSERT INTO `sys_user_role` VALUES ('13', '2', '3');
每个用户有对应的角色,每个角色都有对应的权限
过滤器
在上一篇文章中,我们使用的是 shiro 默认的权限拦截 Filter,而因为 JWT 的整合,我们需要自定义自己的过滤器 JWTFilter,JWTFilter 继承了 BasicHttpAuthenticationFilter,并部分原方法进行了重写
该过滤器主要有三步:
- 检验请求头是否带有 token
((HttpServletRequest) request).getHeader("Token") != null
- 如果带有 token,执行 shiro 的 login() 方法,将 token 提交到 Realm 中进行检验;如果没有 token,说明当前状态为游客状态(或者其他一些不需要进行认证的接口)
- 如果在 token 校验的过程中出现错误,如 token 校验失败,那么我会将该请求视为认证不通过,则重定向到 `/unauthorized/**
@Slf4j
public class JwtFilter extends BasicHttpAuthenticationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
// 判断请求的请求头上是否带上 "Token"
if (((HttpServletRequest) request).getHeader("token") != null) {
// 如果存储,则进入executeLogin方法执行登录,检查 token 是否正确
try {
executeLogin(request, response);
return true;
} catch (AuthenticationException e) {
// token 错误
responseError(response);
}
}
// 如果请求头不存在 token,则可能是执行登录操作,或者是游客状态登录,无需检查token,直接返回true
return true;
}
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader("Token");
JwtToken jwtToken = new JwtToken(token);
// 提交给realm进行登录,如果错误他会抛出一行并捕获。
getSubject(request, response).login(jwtToken);
// 如果没有抛出异常则代表登入成功,返回true
return true;
}
/**
* 将非法请求跳转到 /unauthorized
*/
private void responseError(ServletResponse response) {
try {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.sendRedirect("/unauthorized");
} catch (IOException e) {
log.error(e.getMessage());
}
}
}
Realm 类
public class UserRealm extends AuthorizingRealm {
@Autowired
SysUserServiceImpl sysUserService;
@Autowired
SysRoleServiceImpl sysRoleService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 授权
// 1.获取当前用户信息
Subject subject = SecurityUtils.getSubject();
SysUser sysUser = (SysUser)subject.getPrincipal();
System.out.println("当前授权用户信息:" + sysUser);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 1.获取当前用户的角色
ArrayList<SysRole> userRoles = sysUserService.getUserRoles(sysUser.getId());
System.out.println("当前用户的角色列表:" + userRoles);
Set<String> userRoleSet = new HashSet<>();
userRoles.forEach(userRole -> userRoleSet.add(userRole.getCode()));
// 2.获取角色拥有的权限
ArrayList<Long> longRoles = new ArrayList<>();
userRoles.forEach(userRole -> longRoles.add(userRole.getId()));
ArrayList<SysMenu> userPermission = sysRoleService.getUserPermission(longRoles);
System.out.println("当前用户的角色权限列表:" + userPermission);
Set<String> userPermissionSet = new HashSet<>();
userPermission.forEach(SysMenu -> userRoleSet.add(SysMenu.getPerms()));
info.setRoles(userRoleSet);
info.setStringPermissions(userPermissionSet);
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 认证
// 1.获取 token
String token = (String) authenticationToken.getCredentials();
// 解密获得username,用于和数据库进行对比
String username = JwtUtil.getUsername(token);
if (username == null || !JwtUtil.verify(token, username)) {
throw new AuthenticationException("token认证失败!");
}
SysUser sysUser = sysUserService.getUserByName(username);
if (sysUser == null) {
throw new AuthenticationException("用户不存在");
}
System.out.println("登录成功");
return new SimpleAuthenticationInfo(sysUser, token, "myRealm");
}
}
ShiroConfig 配置类
设置好我们自定义的 filter,并使所有请求通过我们的过滤器,除了我们用于处理未认证请求的 /unauthorized/**
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
// 添加 shiro 的内置过滤器
Map<String, Filter> filterMap = new LinkedHashMap<>();
filterMap.put("jwt", new JwtFilter());
bean.setFilters(filterMap);
// 设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
bean.setUnauthorizedUrl("/unauthorized"); // 无权限时跳转
Map<String, String> filterRuleMap = new HashMap<>();
// 所有的请求通过我们自己的jwt filter
filterRuleMap.put("/**", "jwt");
// 访问 /unauthorized/** 不通过jwt filter
filterRuleMap.put("/unauthorized", "anon");
filterRuleMap.put("/sys-user/login", "anon");
filterRuleMap.put("/captcha", "anon");
bean.setFilterChainDefinitionMap(filterRuleMap);
return bean;
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("UserRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
/*
* 关闭shiro自带的session
*/
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
return securityManager;
}
/**
* 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
* 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可实现此功能
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
/**
* 开启aop注解支持
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
// 自定义realm
@Bean(name = "UserRealm")
public UserRealm userRealm(){
return new UserRealm();
}
}
跨域
@Configuration
public class CorsConfig implements WebMvcConfigurer {
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.addExposedHeader("Authorization");
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
// .allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT")
.maxAge(3600);
}
}
Controller
AuthController 生成验证码,以及定义无权限
@Slf4j
@RestController
public class AuthController extends BaseController {
@Autowired
private Producer producer;
/*
* 图片验证码
* */
@GetMapping("/captcha")
public Result captcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
String code = producer.createText();
String key = UUID.randomUUID().toString(); // 生成uuid
key = "aaaaa";
code = "11111";
BufferedImage image = producer.createImage(code);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", outputStream);
// 图片转为base64
BASE64Encoder encoder = new BASE64Encoder();
String str = "data:image/jpeg;base64,";
String base64Img = str + encoder.encode(outputStream.toByteArray());
// 存储到 redis 中
redisUtil.hset(Const.CAPTCHA_KEY, key, code, 120);
log.info("验证码 -- {} -- {}", key, code);
return Result.succ(
MapUtil.builder()
.put("token", key)
.put("base64Img", base64Img)
.build()
);
}
@RequestMapping("/unauthorized")
public Result unauthorized(){
return Result.succ(401, "没有权限", null);
}
}
SysUserController 登录接口
@RestController
@RequestMapping("/sys-user")
public class SysUserController extends BaseController {
@PostMapping("/login")
public Result login(@RequestBody JSONObject jsonObject, HttpServletRequest request, HttpServletResponse response){
System.out.println();
// 验证码校验
String codeToken = (String) jsonObject.get("token");
String code = (String) jsonObject.get("code");
if (codeToken == null || code == null) {
return Result.succ(400, "验证码不能为空", null);
}
if (!code.equals(redisUtil.hget(Const.CAPTCHA_KEY, codeToken))) {
return Result.succ(400, "验证码错误", null);
}
String username = (String) jsonObject.get("username");
String password = (String) jsonObject.get("password");
// 2.通过用户名,从数据库取用户信息
SysUser user = sysUserService.getUserByName(username);
if(user == null) {
return Result.succ(400, "用户不存在", null);
}
if(!user.getPassword().equals(password)) {
return Result.succ(400, "密码错误", null);
}
// 一次性使用
redisUtil.hdel(Const.CAPTCHA_KEY, codeToken);
response.setHeader("Token", JwtUtil.createToken(username));
return Result.succ(200, "登录成功", "");
}
}
权限控制注解 @RequiresRoles, @RequiresPermissions
这两个注解为我们主要的权限控制注解, 如
// 拥有 admin 角色可以访问
@RequiresRoles("admin")
// 拥有 user 或 admin 角色可以访问
@RequiresRoles(logical = Logical.OR, value = {"user", "admin"})
// 拥有 vip 和 normal 权限可以访问
@RequiresPermissions(logical = Logical.AND, value = {"vip", "normal"})
// 拥有 user 或 admin 角色,且拥有 vip 权限可以访问
@GetMapping("/getVipMessage")
@RequiresRoles(logical = Logical.OR, value = {"user", "admin"})
@RequiresPermissions("vip")
public ResultMap getVipMessage() {
return resultMap.success().code(200).message("成功获得 vip 信息!");
}
当我们写的接口拥有以上的注解时,如果请求没有带有 token 或者带了 token 但权限认证不通过,则会报 UnauthenticatedException 异常,但是我在 ExceptionController 类对这些异常进行了集中处理
@ExceptionHandler(ShiroException.class)
public ResultMap handle401() {
return resultMap.fail().code(401).message("您没有权限访问!");
}
这时,出现 shiro 相关的异常时则会返回
{
"result": "fail",
"code": 401,
"message": "您没有权限访问!"
}
除了以上两种,还有 @RequiresAuthentication ,@RequiresUser 等注解
权限控制
UserController(user 或 admin 可以访问)
在接口上带上 @RequiresRoles(logical = Logical.OR, value = {"user", "admin"})
- vip 权限
再加上@RequiresPermissions("vip")
AdminController(admin 可以访问)
在接口上带上 @RequiresRoles("admin")
GuestController(所有人可以访问)
不做权限处理
作者:Howie_Y
链接:https://www.jianshu.com/p/3c51832f1051
来源:简书
相关的异常时则会返回
{
"result": "fail",
"code": 401,
"message": "您没有权限访问!"
}
除了以上两种,还有 @RequiresAuthentication ,@RequiresUser 等注解
权限控制
UserController(user 或 admin 可以访问)
在接口上带上 @RequiresRoles(logical = Logical.OR, value = {"user", "admin"})
- vip 权限
再加上@RequiresPermissions("vip")
AdminController(admin 可以访问)
在接口上带上 @RequiresRoles("admin")
GuestController(所有人可以访问)
不做权限处理
作者:Howie_Y 链接:https://www.jianshu.com/p/3c51832f1051 来源:简书。
数据库部分来自 https://shimo.im/docs/OnZDwoxFFL8bnP1c/read