权限系统--前后端完全分离

1.权限系统

1.前端使用: vue + elementui + axios + css + html
 2.后端使用: springboot+mybatis-plus +mybatis+druid+shiro+swagger2+redis

2. 登录界面

2.1前端布局

2.1.1创建Login.vue

权限系统--前后端完全分离_第1张图片

2.1.2在Login.vue中写入登录的代码

2.1.3登录界面的方法、数据


 

后端接口:

package com.qy151.system.controller;


import com.qy151.system.entity.User;
import com.qy151.system.vo.CommonResult;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;

/**
 * 

* 用户表 前端控制器 *

* * @author YSH * @since 2022-08-11 */ @RestController @RequestMapping("/system/user") @Api(tags = "用户接口类") public class UserController { @Autowired private RedisTemplate redisTemplate; @GetMapping("getInfo") public CommonResult getInfo(HttpServletRequest request){ String token = request.getHeader("token"); System.out.println(token); //根据token从redis中获取用户信息 ValueOperations forValue = redisTemplate.opsForValue(); User o = (User) forValue.get(token); return new CommonResult(2000,"获取用户信息成功",o); } }

权限系统--前后端完全分离_第28张图片

3 前置路由守卫

 前置路由守卫:就是在路由跳转前加上自己得一些业务代码,在main.js中配置。类似于拦截器

//设置前置路由守卫 to:到哪个路由  from:从哪个路由来  next():放行到指定路由
router.beforeEach((to,from,next)=>{
      //获取跳转得路径
      var path = to.path;
      //判断是否为登录路由路径
      if(path==="/login"){
          console.log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
          //放行
          return next();
      }
      //其他路由路径 判断是否登录过
      var token = sessionStorage.getItem("token");
      if(token){
          return next();
      }
      //跳转登录
     return next("/login");
})

权限系统--前后端完全分离_第29张图片

4. 整合shiro

4.1添加shiro依赖

        
            org.apache.shiro
            shiro-spring-boot-starter
            1.7.0
        

4.2shiro的配置类

package com.qy151.system.config;


import com.qy151.system.filter.LoginFilter;
import com.qy151.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.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
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;

/**
 * @BelongsProject: 0805-springboot-shiro
 * @BelongsPackage: com.qy151.config
 * @unthor : YSH
 * @date : 2022/8/5 18:52
 * @Description: TODO
 */
@Configuration //交于容器管理
public class ShiroConfig {


    //安全认证
    @Bean
    public DefaultWebSecurityManager securityManager(){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        securityManager.setRealm(realm());
        return securityManager;

    }



    //安全数据源 自定义realm
    @Bean
    public Realm realm(){
        MyRealm myRealm=new MyRealm();
        myRealm.setCredentialsMatcher(credentialsMatcher());
        return myRealm;
    }


    //密码匹配器
    @Bean
    public CredentialsMatcher credentialsMatcher(){
        HashedCredentialsMatcher credentialsMatcher=new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("MD5");
        credentialsMatcher.setHashIterations(1024);
        return credentialsMatcher;
    }


    @Autowired
    private RedisTemplate redisTemplate;

    //shiro过滤器工厂 设置过滤的规则
    @Bean(value = "shiroFilter")
    public ShiroFilterFactoryBean filterFactoryBean(){
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager());

        //设置拦截规则
        HashMap map=new HashMap<>();
        map.put("/system/login","anon");
        map.put("/doc.html","anon");
        map.put("/swagger-ui.html","anon");
        map.put("/swagger/**","anon");
        map.put("/webjars/**", "anon");
        map.put("/swagger-resources/**","anon");
        map.put("/v2/**","anon");
        map.put("/static/**", "anon");

        map.put("/**","authc");



        factoryBean.setFilterChainDefinitionMap(map);

        //设置自定义认证过滤器
        HashMap filterMap=new HashMap();
        filterMap.put("authc",new LoginFilter(redisTemplate));
        factoryBean.setFilters(filterMap);

        return factoryBean;
    }


    //注册filter
    @Bean
    public FilterRegistrationBean filterRegistrationBean(){
        FilterRegistrationBean filterRegistrationBean=new FilterRegistrationBean<>();
        filterRegistrationBean.setName("shiroFilter");
        filterRegistrationBean.setFilter(new DelegatingFilterProxy());
        filterRegistrationBean.addUrlPatterns("/*");
        return filterRegistrationBean;
    }

    //开始shiro注解
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }
    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

}

4.3增加一个realm类对象

package com.qy151.system.realm;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qy151.system.entity.User;
import com.qy151.system.service.IUserService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @BelongsProject: 0805-springboot-shiro
 * @BelongsPackage: com.qy151.realm
 * @unthor : YSH
 * @date : 2022/8/5 18:57
 * @Description: TODO
 */
public class MyRealm extends AuthorizingRealm {
    @Autowired
    private IUserService userService;
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        QueryWrapper wrapper = new QueryWrapper<>();
        wrapper.eq("username",username);
        wrapper.eq("is_deleted",0);
        User user = userService.getOne(wrapper);
        if(user!=null){
            ByteSource bytes = ByteSource.Util.bytes(user.getSalt());
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),bytes,this.getName());
            return info;
        }
        return null;
    }
}

4.4修改controller代码

package com.qy151.system.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qy151.system.entity.User;
import com.qy151.system.service.IUserService;
import com.qy151.system.vo.CommonResult;
import com.qy151.system.vo.LoginVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.*;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @BelongsProject: 0811qxglxt
 * @BelongsPackage: com.qy151.system.controller
 * @unthor : YSH
 * @date : 2022/8/11 16:17
 * @Description: TODO
 */
@RestController
@RequestMapping("system")
@Api(tags = "登录接口类")
public class LoginController {
    @Autowired
    private IUserService userService;

    @Autowired
    private RedisTemplate redisTemplate;

    @PostMapping("login")
    @ApiOperation(value="登录接口")
    public CommonResult login(@RequestBody LoginVo loginVo){
        try{
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(loginVo.getName(), loginVo.getPassword(),loginVo.getSalt());
            subject.login(usernamePasswordToken);

            Object one = subject.getPrincipal();

            String token = UUID.randomUUID().toString();
            ValueOperations forValue = redisTemplate.opsForValue();
            forValue.set(token,one,24,TimeUnit.HOURS);
            return new CommonResult(2000,"登录成功",token);
        }catch (Exception e){
            return new CommonResult(5000,"登录失败",null);
        }
    }

}

权限系统--前后端完全分离_第30张图片

4.5测试登录

权限系统--前后端完全分离_第31张图片

登录成功后获取用户信息时出现如下得错误

权限系统--前后端完全分离_第32张图片

 被shiro得拦截器给拦截器了。

package com.qy151.system.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.qy151.system.vo.CommonResult;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;

/**
 * @BelongsProject: 0805-springboot-shiro
 * @BelongsPackage: com.qy151.filter
 * @unthor : YSH
 * @date : 2022/8/5 19:19
 * @Description: TODO
 */
//如果类没有交于spring容器来管理 那么该类中得属性也不能让spring帮你注入
public class LoginFilter extends FormAuthenticationFilter {

    @Autowired
    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;
        //判断请求方式是否为OPTIONS
        String method = req.getMethod();
        if (method!=null && method.equals("OPTIONS")){
            return true;
        }
        //判断请求头是否有token值
        String token = req.getHeader("token");
        if (token!=null && redisTemplate.hasKey(token)){
            return true;
        }

        //返回true则表示登录  返回false则表示未登录
        System.out.println("=====================");
        return false;
    }


    //未登录时调用该方法? 为什么进入没有登录方法:
    // --->第一个请求是OPTIONS,没有携带token  第二个因为前端和后端不是用得同一个session.默认shiro以sessionId为是否登录得标准
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        CommonResult commonResult = new CommonResult(4001, "未登录", null);
        ObjectMapper objectMapper=new ObjectMapper();
        String json = objectMapper.writeValueAsString(commonResult);
        writer.print(json); //响应给客户json数据
        writer.flush();
        writer.close();
        return false;
    }
}

权限系统--前后端完全分离_第33张图片

5.主页布局





权限系统--前后端完全分离_第34张图片

5.1退出

5.1.1前端:

由于每个跳转都需要写ip和端口,我们可以在main.js中设置axios的基础路径

//设置axios基础路径
axios.defaults.baseURL="http://localhost:8809"

权限系统--前后端完全分离_第35张图片

权限系统--前后端完全分离_第36张图片

5.1.2后端接口

@GetMapping("logout")
    @ApiOperation(value = "退出接口")
    public CommonResult logout(HttpServletRequest request){
        String token = request.getHeader("token");
        if (redisTemplate.hasKey(token)) {
            redisTemplate.delete(token);
            return new CommonResult(2000, "退出成功", null);
        }
        return new CommonResult(5000,"退出失败",null);
    }

6.查询左侧菜单

6.1前端方法

initLeftMenu(){
                this.$http.get("/system/permission/leftMenu").then(result=>{
                      if(result.data.code===2000){
                           this.leftMenus=result.data.data;
                      }
                })
            },

权限系统--前后端完全分离_第37张图片

6.2后端

6.2.1Controller层

package com.qy151.system.controller;


import com.qy151.system.service.IPermissionService;
import com.qy151.system.vo.CommonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

/**
 * 

* 权限 前端控制器 *

* * @author YSH * @since 2022-08-11 */ @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.findPermissionByUserId(token); } }

6.2.2Service层

package com.qy151.system.service;

import com.qy151.system.entity.Permission;
import com.baomidou.mybatisplus.extension.service.IService;
import com.qy151.system.vo.CommonResult;

/**
 * 

* 权限 服务类 *

* * @author YSH * @since 2022-08-11 */ public interface IPermissionService extends IService { CommonResult findPermissionByUserId(String token); }
package com.qy151.system.service.impl;

import com.qy151.system.entity.Permission;
import com.qy151.system.entity.User;
import com.qy151.system.mapper.PermissionMapper;
import com.qy151.system.service.IPermissionService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qy151.system.service.IRolePermissionService;
import com.qy151.system.vo.CommonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * 

* 权限 服务实现类 *

* * @author YSH * @since 2022-08-11 */ @Service public class PermissionServiceImpl extends ServiceImpl implements IPermissionService { @Autowired private PermissionMapper permissionMapper; @Autowired private RedisTemplate redisTemplate; @Autowired private IRolePermissionService rolePermissionService; @Override public CommonResult findPermissionByUserId(String token) { //根据token获取用户信息 ValueOperations forValue = redisTemplate.opsForValue(); User o = (User) forValue.get(token); String id = o.getId(); //根据用户id查询该用户具有得权限。 List permissionList = permissionMapper.selectByUserId(id); //设置层级关系 List firstMenus = new ArrayList<>(); for (Permission first : permissionList) { //int if (first.getPid().equals("1")) { firstMenus.add(first); } } //为一级菜单设置二级菜单 for (Permission first : firstMenus) { //根据一级菜单id 查询 该菜单得二级菜单。如果出现不确定有几级菜单 那么我们可以使用方法得递归调用 first.setChildren(findChildren(permissionList, first.getId())); } return new CommonResult(2000,"查询成功",firstMenus); } //方法递归 private List findChildren(List permissionList, String id) { List 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; } }

6.2.4实体类添加列

权限系统--前后端完全分离_第38张图片

6.2.5Sql语句





    

6.2.6前端界面

权限系统--前后端完全分离_第39张图片

 

权限系统--前后端完全分离_第40张图片 






6.2.7查看菜单

权限系统--前后端完全分离_第41张图片

7.管理角色

7.1查看全部角色信息

查询全部角色信息时,添加了分页插件

插件的配置类:

权限系统--前后端完全分离_第42张图片

package com.qy151.system.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusConfig {

    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

}

7.1.1Controller层

@PostMapping("findRole/{currentPage}/{pageSize}")
    @ApiOperation(value = "查询接口")
    public CommonResult findRole(@PathVariable Integer currentPage, @PathVariable Integer pageSize, @RequestBody RoleVo roleVo){
        return roleService.selectRole(currentPage,pageSize,roleVo);
    }

7.1.2Service层

CommonResult selectRole(Integer currentPage, Integer pageSize, RoleVo roleVo);
@Autowired
    private RoleMapper roleMapper;

    @Override
    public CommonResult selectRole(Integer currentPage, Integer pageSize, RoleVo roleVo) {
        Page page = new Page<>(currentPage,pageSize);

        QueryWrapper wrapper = new QueryWrapper<>();

        if (StringUtils.hasText(roleVo.getRoleName())){
            wrapper.like("role_name",roleVo.getRoleName());
        }

        if (StringUtils.hasText(roleVo.getStartDate())){
            wrapper.ge("gmt_create",roleVo.getStartDate());
        }

        if (StringUtils.hasText(roleVo.getEndDate())){
            wrapper.le("gmt_create",roleVo.getEndDate());
        }

        roleMapper.selectPage(page,wrapper);

        return new CommonResult(2000,"查询成功",page);
    }

7.1.3 前端界面

未完待续!!!!!!

你可能感兴趣的:(Vue,前后端分离,Idea,WebStorm)