之前的博文已经对shiro在springboot中的使用做了详细说明,这里继续说明如何具体实施,由于我一直在完善自已编写的博客,这里做一个笔记。
1. 创建表
我创建了4个表来对不同身份的用户进行权限管理,主要是通过用户->角色->权限进行管理
各个表的样子如下:
2. 定义Perm类
这里的类与之前创建的perm表相对应
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Perm implements Serializable {
private Long permId;
private String permission;
}
同时在User类中增加roleId属性
3.创建PermService进行管理
这里主要创建了两个方法
public interface PermService {
//根据permId查询perm
public Perm queryById(Long id);
//根据username查询所有permission
public List<Perm> queryByUserName(String username);
}
主要的对应的sql语句如下,相关的PermDao,PermServcieImpl这里就不再赘述:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--查官网https://mybatis.org/mybatis-3/zh/getting-started.html-->
<!--映射到Dao文件-->
<mapper namespace="com.xinxin.dao.PermDao">
<!-- 开启二级缓存-->
<cache />
<select id="queryByUserName" parameterType="String" resultType="Perm">
<!--这里一次性连接了四个表-->
SELECT permission FROM users,roles,role_perm,perms WHERE
users.user_id = roles.role_id AND
roles.role_id = role_perm.role_id AND
role_perm.perm_id = perms.perm_id AND
username= #{username}
</select>
<select id="queryById" resultType="Perm">
select * from perms where perm_id = #{permId}
</select>
</mapper>
4. 登录时直接获取权限
@PostMapping("/api/login")
public Object toLogin(@RequestBody JSONObject param) {
//取出content
String username = param.getString("username");
String password=param.getString("password");
//从SecurityUtils里边创建一个subject
Subject subject = SecurityUtils.getSubject();
//在认证提交前准备token(令牌)
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//自定义返加的token,登录后自动分配一个线程Id
String token_res = subject.getSession().getId().toString();
Map<String,Object> map=new HashMap<>();
map.put("token",token_res);
//获取权限List
List<Perm> perms = permService.queryByUserName(username);
if(perms.size()>0){
//过滤掉Perm类中的permId
List<String> list = perms.stream().map(perm -> perm.getPermission()).distinct().collect(Collectors.toList());
map.put("perm",list);
}else{
map.put("perm",null);
}
//执行shiro认证登陆,抛出的异常在GlobalExceptionHandler中处理
subject.login(token);
if (subject.isAuthenticated()) {
return ApiResult.succ(map);
} else {
token.clear();
return new ResponseEntity<>("登录失败", HttpStatus.NOT_FOUND);
}
}
关于shiro登录验证的操作,我之前的博客已经讲得非常详细,这里主要是将获取到的权限返回给前端。
通过Postman进行测试
可以看到成功取到了admin对应的权限。
5. 将权限保存在Vuex的state中
Vuex的操作方法见https://www.jianshu.com/p/a804606ad8e9,这里不再赘述。
5.1 让state持久化
我想把用户登录获得的权限保存在Vuex的state中,但是只要刷新页面,之前存入state中的数值就会消失,为了解决这个问题我参考了这位网友的做法,引入vuex-persistedstate插件
- 安装
npm install vuex-persistedstate --save
- store/index.js中将persistedstate引入
import Vue from "vue";
import Vuex from "vuex";
//引入vuex-persistedstate插件
import createPersistedState from "vuex-persistedstate"
Vue.use(Vuex);
export default new Vuex.Store({
state: {
//测试初始值
products: 'haha'
},
mutations: {
//通过调用minusPrice将state中products值替换为payload
minusPrice (state, payload ) {
state.products=payload
}
},
actions: {},
modules: {},
//这里是将state持久化到localstorage,也就是本地
plugins: [createPersistedState()]
});
在Vue页面中操作
//调用该命令改变state的值,将products改变为2
this.$store.commit('minusPrice', 2);
//通过调用该命令直接获取state的值
this.msg=this.$store.state.products
5.2 测试
编写了一个测试页面验证刷新页面是否对state有影响,改变state之前:
点击按钮触发minusPrice()改变state为2
说明state已经持久化到了本地。
6.总结
这里将后端获取的用户权限持久化到了Vuex中的state中,就可以在任何一个页面取到state值,从而可以利用Vue中的v-if操作决定用户能够访问的页面元素了,这样前端与后端都实现了权限控制。