1.添加前置路由守卫
前置路由守卫:就是在路由跳转前加上自己得一些业务代码。(放在main.js中)
//前置路由守卫 to:到某个路由 from 从哪个路由出来 next() 放行到指定的路由
router.beforeEach((to,from,next)=>{
//获取跳转的路径
var path = to.path;
//判断是否为登录路由路径
if(path==="/login"){
//放行
return next();
}
//其他路由路径 判断是否登录过
var token = sessionStorage.getItem("token");
if(token){
return next();
}
//跳转登录
return next("/login");
})
2.整合shiro实现登录
1.添加shiro依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.7.0</version>
</dependency>
2.增加一个realm类对象
public class MyRealm extends AuthorizingRealm {
@Autowired
private IUserService userServicet;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
/* User primaryPrincipal = (User) principals.getPrimaryPrincipal();
//根据账号查询该用户具有哪些权限
List<String> list = userServicet.findPermission(primaryPrincipal.getUserid());
if (list!=null&&list.size()>0){
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(list);
return info;
}*/
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//1.根据token获取账号
String username = (String) token.getPrincipal();
//2.根据账号查询用户信息
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username",username);
wrapper.eq("is_deleted",0);
User user = userServicet.getOne(wrapper);
if (user!=null){
//从数据库中获取的密码
ByteSource byteSource = ByteSource.Util.bytes(user.getSalt()); //获取盐
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),byteSource,this.getName());
return info;
}
return null;
}
}
3.shiro的配置类
package com.zsy.system.config;
import com.zsy.system.filter.LoginFilter;
import com.zsy.system.realm.MyRealm;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.filter.DelegatingFilterProxy;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.Map;
/**
* @Author zsy
* @Date 2022/8/5 18:36
* @PackageName:com.zsy.config
* @ClassName: ShiroConfig
* @Version 1.0
*/
@Configuration
public class ShiroConfig {
@Bean
public DefaultWebSecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm());
return securityManager;
}
@Bean
public Realm realm(){
MyRealm myRealm = new MyRealm();
myRealm.setCredentialsMatcher(credentialsMatcher());
return myRealm;
}
@Bean
public CredentialsMatcher credentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashIterations(1024);
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
return hashedCredentialsMatcher;
}
@Autowired
private RedisTemplate redisTemplate;
@Bean("shiroFilter")
public ShiroFilterFactoryBean filterFactoryBean(){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager());
//设置shiro过滤规则
Map<String, String> map = new HashMap<>();
map.put("/system/login","anon");
map.put("/doc.html","anon");
map.put("/swagger-ui.html", "anon");
map.put("/webjars/**", "anon");
map.put("/swagger-resources/**", "anon");
map.put("/swagger/**", "anon");
map.put("/swagger2/**", "anon");
map.put("/v2/**", "anon");
map.put("/**","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
//设置未登录过滤器
Map<String, Filter> filters = new HashMap<>();
filters.put("authc",new LoginFilter(redisTemplate));
shiroFilterFactoryBean.setFilters(filters);
return shiroFilterFactoryBean;
}
@Bean
public FilterRegistrationBean<Filter> filterProxy(){
FilterRegistrationBean<Filter> filterRegistrationBean=new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new DelegatingFilterProxy());
filterRegistrationBean.setName("shiroFilter");
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
}
4.修改controller代码
?登录成功后获取用户信息时出现如下得错误
被shiro得拦截器给拦截器了。
//如果类没有交于spring容器来管理 那么该类中得属性也不能让spring帮你注入
public class LoginFilter extends FormAuthenticationFilter {
private RedisTemplate redisTemplate; //LoginFilter必须交于spring容器来管理。
public LoginFilter(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
//当登录成功后执行得方法,如果该方法返回false,则执行onAccessDenied
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
System.out.println(redisTemplate);
HttpServletRequest req = (HttpServletRequest) request;
//1.请求方式是否为OPTIONS
String method = req.getMethod();
if(method!=null && method.equals("OPTIONS")){
return true;
}
//2.判断请求头是否有token值
String token = req.getHeader("token");
if(token!=null && redisTemplate.hasKey(token)){
return true;
}
return false;
}
//未登录时调用该方法? 为什么进入没有登录方法:
// --->第一个请求是OPTIONS,没有携带token 第二个因为前端和后端不是用得同一个session.默认shiro以sessionId为是否登录得标准
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
System.out.println("*******************************");
return false;
}
}
?3.主页布局
<template>
<el-container>
<el-header>
<span id="logo" style="display: inline-block;width: 50%;height: 100%;float: left" >
<a href="https://www.bilibili.com/video/BV14g41197PY/"><img src="../assets/logo.png" height="100%" width="180px"></a>
</span>
<span id="avatar" style="float: right">
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link" style="margin-top: 10px; display: inline-block;">
<el-avatar ></el-avatar>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="info">个人信息</el-dropdown-item>
<el-dropdown-item command="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</span>
</el-header>
<el-container>
<el-aside width="200px">
</el-aside>
<el-main>
</el-main>
</el-container>
<el-footer>Footer</el-footer>
</el-container>
</template>
<script>
export default {
name: "Home",
methods:{
getInfo(){
this.$http.get("http://localhost:8808/system/user/getInfo").then(result=>{
console.log(result)
})
}
}
}
</script>
<!--当前vue有效-->
<style>
html,body,#app{
height: 100%;
}
body,#app{
padding: 0px;
margin:0px;
}
.el-container{
height: 100%;
}
.el-header, .el-footer {
background-color: #1F272F;
color: #333;
line-height: 60px;
}
.el-aside {
background-color: #545c64;
color: #333;
line-height: 560px;
}
.el-aside>.el-menu{
border: none;
}
.el-main {
background-color: #E9EEF3;
color: #333;
line-height: 560px;
}
body > .el-container {
margin-bottom: 40px;
}
.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
line-height: 260px;
}
.el-container:nth-child(7) .el-aside {
line-height: 320px;
}
</style>
4.退出功能实现
前端代码?
//下拉触发事件
handleCommand(command){
if(command==='logout'){
this.$axios.get("/system/logout").tehn(result=>{
if(result.data.code===2000){
//移除存储的token
sessionStorage.removeItem("token");
this.$axios.push("/login")
}
})
}
},
后端代码
@GetMapping("/logout")
@ApiOperation(value = "退出接口")
public CommonResult logout(HttpServletRequest request){
//获取登录者唯一的token
String token = request.getHeader("token");
if(redisTemplate.hasKey(token)){
redisTemplate.delete(token);
return new CommonResult(2000,"退出成功",null);
}
return new CommonResult(5000,"无效的token",null);
}
5.左侧菜单
1.使用递归完成前端
components创建左侧菜单组件
<template>
<div class="navMenu">
<template v-for="navMenu in menuData">
<!-- 最后一级菜单 叶子菜单 -->
<el-menu-item v-if="navMenu.children.length==0"
:index="navMenu.path"
>
<i :class="navMenu.icon"></i>
<span slot="title">{{navMenu.name}}</span>
</el-menu-item>
<!-- 此菜单下还有子菜单 -->
<el-submenu v-if="navMenu.children.length!=0"
:index="navMenu.path">
<template slot="title">
<i :class="navMenu.icon"></i>
<span> {{navMenu.name}}</span>
</template>
<!-- 递归 -->
<LeftMenu :menuData="navMenu.children"></LeftMenu>
</el-submenu>
</template>
</div>
</template>
<script>
export default {
name: 'LeftMenu',
//接受使用者传递的数据
props: ['menuData'],
data() {
return {}
},
methods: {}
}
</script>
<style>
</style>
在Home组件中使用LeftMenu组件
?
<template>
<el-container>
<el-header>
<span id="logo" style="display: inline-block;width: 50%;height: 100%;float: left" >
<a href="https://www.bilibili.com/video/BV14g41197PY/"><img src="../assets/logo.png" height="100%" width="180px"></a>
</span>
<span id="avatar" style="float: right">
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link" style="margin-top: 10px; display: inline-block;">
<el-avatar ></el-avatar>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="info">个人信息</el-dropdown-item>
<el-dropdown-item command="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</span>
</el-header>
<el-container>
<el-aside width="200px">
<el-menu
default-active="2"
class="el-menu-vertical-demo"
background-color="#545c64"
text-color="#fff"
:router="true"
unique-opened="unique-opened"
active-text-color="#ffd04b">
<LeftMenu :menu-data="leftMenus"></LeftMenu>
</el-menu>
</el-aside>
<el-main>
<!--视图渲染-->
<router-view/>
</el-main>
</el-container>
<el-footer>Footer</el-footer>
</el-container>
</template>
<script>
import LeftMenu from "@/components/LeftMenu";
export default {
name: "Home",
components: {LeftMenu},
comments:{
LeftMenu
},
data(){
return{
leftMenus:[],
}
},
created() {
this.initLeftMenu();
},
methods:{
initLeftMenu(){
this.$axios.get("/system/permission/leftMenu").then(result=>{
if(result.data.code===2000){
this.leftMenus=result.data.data;
}
})
},
getInfo(){
this.$axios.get("http://localhost:8809/system/user/getInfo").then(result=>{
console.log(result)
})
},
//下拉触发事件
handleCommand(command){
if(command==='logout'){
this.$axios.get("/system/logout").then(result=>{
if(result.data.code===2000){
//移除存储的token
sessionStorage.removeItem("token");
this.$router.push("/login")
}
})
}
},
}
}
</script>
<!--当前vue有效-->
<style>
html,body,#app{
height: 100%;
}
body,#app{
padding: 0px;
margin:0px;
}
.el-container{
height: 100%;
}
.el-header, .el-footer {
background-color: #1F272F;
color: #333;
line-height: 60px;
}
.el-aside {
background-color: #545c64;
color: #333;
line-height: 560px;
}
.el-aside>.el-menu{
border: none;
}
.el-main {
background-color: #E9EEF3;
color: #333;
line-height: 560px;
}
body > .el-container {
margin-bottom: 40px;
}
.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
line-height: 260px;
}
.el-container:nth-child(7) .el-aside {
line-height: 320px;
}
</style>
后端
先在 Permission实体类定义一个 children属性
controller层:
@RestController
@RequestMapping("/system/permission")
public class PermissionController {
@Autowired
private IPermissionService permissionService;
@GetMapping("/leftMenu")
public CommonResult leftMenu(HttpServletRequest request){
String token = request.getHeader("token");
return permissionService.findPermissionById(token);
}
}
service层:
@Service
public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permission> implements IPermissionService {
@Autowired
private PermissionMapper permissionMapper;
@Autowired
private RedisTemplate redisTemplate;
@Override
public CommonResult findPermissionById(String token) {
//根据token获取用户信息
ValueOperations forValue = redisTemplate.opsForValue();
User user = (User) forValue.get(token);
String id = user.getId();
//根据用户的id查询用户的权限
List<Permission> lsit = permissionMapper.selectPermissionById(id);
//设置层级关系
List<Permission> firstMenus = new ArrayList<>();
for(Permission first : lsit){
if(first.getPid().equals("1"));
firstMenus.add(first);
}
//设置二级菜单
for(Permission first : firstMenus){
//根据一级菜单可以查询二级菜单 若不确定有几级菜单 那可以使用递归调用
first.setChildren(findChildren(lsit,first.getId()));
}
return new CommonResult(2000,"查询成功",firstMenus);
}
//方法递归
private List<Permission> findChildren(List<Permission> permissionList,String id){
List<Permission> children = new ArrayList<>();
for(Permission p : permissionList){
if(p.getPid().equals(id)){
children.add(p);
}
}
for(Permission child : children){
child.setChildren(findChildren(permissionList,child.getId()));
}
return children;
}
}
sql语句:?
select distinct p.* from acl_user_role ur join acl_role_permission rp on ur.role_id=rp.role_id join acl_permission p on rp.permission_id=p.id
where ur.id=#{id} and type=1 and rp.is_deleted=0
表结构:?
|