基于Shiro+Springboot+Mybatis+Vue权限管理系统(精)

代码上传到了github

后端:GitHub - chenqi13814529300/my-csdn-shiro

前端:GitHub - chenqi13814529300/my-shiro-ui

sql代码在后端代码里,这里没有用jpa,因为易上手,难精通,比较抽象,不利于未来发展

目录

数据库设计

 后端项目构建

创建项目

pom依赖

application 

实现效果

具体实现

整体目录

entity类

dto层,用于封装前端传来的用户名和密码

Mapper.xml层跟数据库相关

dao层,也就是mapper接口层

service层,业务处理层

contorller层

config

auth

utils

hander

启动类 

前端项目构建

创建项目

前端具体实现

目录如下

main.js router.js token.js 

api

views 

流程思路


数据库设计

学到这个阶段,大家Mysql应该不陌生了,既然是走Mybatis的路线,先搭建一个健壮的数据结构很重要,秉着高内聚低耦合去设计。

不可取的设计方式如下:

Id    username     password     role    permission

我建立如下数据结构,一组是用户角色权限,一组是角色的token

用户角色权限的一组表

user

基于Shiro+Springboot+Mybatis+Vue权限管理系统(精)_第1张图片

 user_role

基于Shiro+Springboot+Mybatis+Vue权限管理系统(精)_第2张图片

role

基于Shiro+Springboot+Mybatis+Vue权限管理系统(精)_第3张图片

 role_permission

基于Shiro+Springboot+Mybatis+Vue权限管理系统(精)_第4张图片

permission 

基于Shiro+Springboot+Mybatis+Vue权限管理系统(精)_第5张图片

角色token存储的表 

user_token (这些数据是生成的,你们设计个表即可)

基于Shiro+Springboot+Mybatis+Vue权限管理系统(精)_第6张图片

 后端项目构建

创建项目

按步骤来即可

基于Shiro+Springboot+Mybatis+Vue权限管理系统(精)_第7张图片

 基于Shiro+Springboot+Mybatis+Vue权限管理系统(精)_第8张图片

 基于Shiro+Springboot+Mybatis+Vue权限管理系统(精)_第9张图片

基于Shiro+Springboot+Mybatis+Vue权限管理系统(精)_第10张图片

pom依赖

  
        
            org.apache.shiro
            shiro-spring
            1.3.2
        
        
        
            org.apache.shiro
            shiro-ehcache
            1.4.0
        

        
            org.springframework.boot
            spring-boot-starter-freemarker
        

        
            org.springframework.boot
            spring-boot-starter-aop
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.4.2
        

        
            mysql
            mysql-connector-java
            runtime
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
            
                
                    org.junit.vintage
                    junit-vintage-engine
                
            
        

        
            commons-lang
            commons-lang
            2.6
        

application 

server:
  port: 9000
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/myshiro?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
    username: root
    password: 123
    name: defaultDataSource

建立目录

基于Shiro+Springboot+Mybatis+Vue权限管理系统(精)_第11张图片

auth包下放权限过滤器,判断token等

cofig包下是shiro的配置文件

dto包下是前端传入后端的用户名和密码的实体类

handler包下是全局异常处理类

其他的你们应该知道了

实现效果

 不同用户登录进去有不同的权限

基于Shiro+Springboot+Mybatis+Vue权限管理系统(精)_第12张图片

登录后如下

基于Shiro+Springboot+Mybatis+Vue权限管理系统(精)_第13张图片

具体实现

整体目录

基于Shiro+Springboot+Mybatis+Vue权限管理系统(精)_第14张图片

entity类

User

package com.mycsdnshiro.mycsdnshiro.entity;

public class User {

    private Integer userId;
    private String username;
    private String password;
    private Integer roleId;
    private String roleName;
    private String permission;
    private Integer permissionId;

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", roleId=" + roleId +
                ", roleName='" + roleName + '\'' +
                ", permission='" + permission + '\'' +
                ", permissionId=" + permissionId +
                '}';
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getRoleId() {
        return roleId;
    }

    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getPermission() {
        return permission;
    }

    public void setPermission(String permission) {
        this.permission = permission;
    }

    public Integer getPermissionId() {
        return permissionId;
    }

    public void setPermissionId(Integer permissionId) {
        this.permissionId = permissionId;
    }

    public User() {
    }

    public User(Integer userId, String username, String password, Integer roleId, String roleName, String permission, Integer permissionId) {
        this.userId = userId;
        this.username = username;
        this.password = password;
        this.roleId = roleId;
        this.roleName = roleName;
        this.permission = permission;
        this.permissionId = permissionId;
    }
}

UserToken

package com.mycsdnshiro.mycsdnshiro.entity;

import java.time.LocalDateTime;

public class UserToken {


    private Integer userId;

    private String token;

//    过期时间
    private LocalDateTime expireTime;


//    更新时间
    private LocalDateTime updateTime;

    @Override
    public String toString() {
        return "UserTokenMapper{" +
                "userId=" + userId +
                ", token='" + token + '\'' +
                ", expireTime=" + expireTime +
                ", updateTime=" + updateTime +
                '}';
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public LocalDateTime getExpireTime() {
        return expireTime;
    }

    public void setExpireTime(LocalDateTime expireTime) {
        this.expireTime = expireTime;
    }

    public LocalDateTime getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(LocalDateTime updateTime) {
        this.updateTime = updateTime;
    }

    public UserToken(Integer userId, String token, LocalDateTime expireTime, LocalDateTime updateTime) {
        this.userId = userId;
        this.token = token;
        this.expireTime = expireTime;
        this.updateTime = updateTime;
    }

    public UserToken() {
    }
}

RoleAndPermission

package com.mycsdnshiro.mycsdnshiro.entity;

public class RoleAndPermission {
    private Integer userId;
    private Integer roleId;
    private String roleName;
    private String permission;

    public RoleAndPermission(Integer userId, Integer roleId, String roleName, String permission) {
        this.userId = userId;
        this.roleId = roleId;
        this.roleName = roleName;
        this.permission = permission;
    }

    @Override
    public String toString() {
        return "RoleAndPermission{" +
                "userId=" + userId +
                ", roleId=" + roleId +
                ", roleName='" + roleName + '\'' +
                ", permission='" + permission + '\'' +
                '}';
    }

    public RoleAndPermission() {
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public Integer getRoleId() {
        return roleId;
    }

    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getPermission() {
        return permission;
    }

    public void setPermission(String permission) {
        this.permission = permission;
    }
}

dto层,用于封装前端传来的用户名和密码

package com.mycsdnshiro.mycsdnshiro.dto;

public class LoginDTO {
    private String username;
    private String password;

    public LoginDTO() {
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public LoginDTO(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @Override
    public String toString() {
        return "LoginDTO{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

Mapper.xml层跟数据库相关

UserMapper

这些代码都没什么必要说的,来看此文章你们应该会增删改查了吧




    
    
        
        
        
        
    
    
        
        
        
        
        

    

    

    

    





UserTokenMapper




    
    
        
        
        
        
    

    

    

    
        insert into user_token
            (user_id,token,update_time,expire_time)
        values(#{userId},#{token},#{updateTime},#{expireTime})
    

    
        UPDATE user_token
        set expire_time=#{expireTime},
            token=#{token},
            update_time=#{updateTime}
        where user_id=#{userId};
    




dao层,也就是mapper接口层

UserMapper

package com.mycsdnshiro.mycsdnshiro.mapper;

import com.mycsdnshiro.mycsdnshiro.entity.RoleAndPermission;
import com.mycsdnshiro.mycsdnshiro.entity.User;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface UserMapper {
    User findByUsername(String username);
    User findByUserId(Integer userId);

    List rolePermissionByRoleId(Integer roleId);
}

UserTokenMapper

package com.mycsdnshiro.mycsdnshiro.mapper;

import com.mycsdnshiro.mycsdnshiro.entity.UserToken;

public interface UserTokenMapper {

    UserToken findByToken(String token);

    UserToken findByUserId(Integer userId);

    int  insertUserToken(UserToken userToken);
    int updateUserToken(UserToken userToken);
}

service层,业务处理层

你们将明白为什么要多这一层,而不直接controller调用mapper接口

ShiroLoginService

package com.mycsdnshiro.mycsdnshiro.service;

import com.mycsdnshiro.mycsdnshiro.entity.RoleAndPermission;
import com.mycsdnshiro.mycsdnshiro.entity.User;
import com.mycsdnshiro.mycsdnshiro.entity.UserToken;

import java.util.List;
import java.util.Map;

public interface ShiroLoginService {

    List queryAllUser();

    User findByUsername(String username);

    Map createToken(Integer userId);

    void logout(String token);

    UserToken findByToken(String accessToken);


    User findByUserId(Integer userId);

    List rolePermissionByRoleId(Integer roleId);

}

ShiroLoginServiceImpl

package com.mycsdnshiro.mycsdnshiro.service.impl;

import com.mycsdnshiro.mycsdnshiro.auth.TokenGenerator;
import com.mycsdnshiro.mycsdnshiro.entity.RoleAndPermission;
import com.mycsdnshiro.mycsdnshiro.entity.User;
import com.mycsdnshiro.mycsdnshiro.entity.UserToken;
import com.mycsdnshiro.mycsdnshiro.mapper.UserMapper;
import com.mycsdnshiro.mycsdnshiro.mapper.UserTokenMapper;
import com.mycsdnshiro.mycsdnshiro.service.ShiroLoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class ShiroLoginServiceImpl implements ShiroLoginService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserTokenMapper userTokenMapper;
    private  final  static  int EXPIRE=12;

    @Override
    public List queryAllUser() {
        return null;
    }

    @Override
    public User findByUsername(String username) {
        return userMapper.findByUsername(username);
    }

//    给当前登录的用户生成token
    @Override
    public Map createToken(Integer userId) {
        Map result=new HashMap<>();
//        生成一个token,代表这个用户的标识
        String token = TokenGenerator.generateValue();
//        当前时间
        LocalDateTime now = LocalDateTime.now();
//        过期时间
        LocalDateTime expireTime = now.plusHours(EXPIRE);

//        通过Id 判断是否生成过token 若有则更新,若没有则添加
        UserToken tokenEntity = userTokenMapper.findByUserId(userId);

        if(tokenEntity==null){
//            新增
            tokenEntity = new UserToken();
            tokenEntity.setUserId(userId);
            tokenEntity.setToken(token);
            tokenEntity.setUpdateTime(now);
            tokenEntity.setExpireTime(expireTime);
//            新增就sql添加
            userTokenMapper.insertUserToken(tokenEntity);

        }else {
//            更新
            tokenEntity.setToken(token);
            tokenEntity.setUpdateTime(now);
            tokenEntity.setExpireTime(expireTime);
//            更新就sql更新
            userTokenMapper.updateUserToken(tokenEntity);

        }
        result.put("token",token);
        result.put("expire",expireTime);
        return result;

    }

    @Override
    public void logout(String token) {

    }

    @Override
    public UserToken findByToken(String accessToken) {
        return userTokenMapper.findByToken(accessToken);
    }

    @Override
    public User findByUserId(Integer userId) {
        return userMapper.findByUserId(userId);
    }

    @Override
    public List rolePermissionByRoleId(Integer roleId) {
        return userMapper.rolePermissionByRoleId(roleId);
    }
}

contorller层

ShiroLoginController     

用于用户登录的

package com.mycsdnshiro.mycsdnshiro.controller;


import com.mycsdnshiro.mycsdnshiro.dto.LoginDTO;
import com.mycsdnshiro.mycsdnshiro.entity.User;
import com.mycsdnshiro.mycsdnshiro.service.ShiroLoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/user")
public class ShiroLoginController {
@Autowired
private ShiroLoginService shiroLoginService;

    @PostMapping("login")
    public Map login(@RequestBody @Validated LoginDTO loginDTO, BindingResult bindingResult) {
//       把后端处理后的数据,封装在里面,返回给前端
        Map result = new HashMap<>();

//前端返回值校验
        if (bindingResult.hasErrors()) {
            result.put("status", 400);
            result.put("msg", bindingResult.getFieldError().getDefaultMessage());
            return result;
        }

//        获取登录前端传入的值
        String username = loginDTO.getUsername();
        String password = loginDTO.getPassword();
        System.out.println(username);
//        在数据库中查找是否有此用户名
        User user = shiroLoginService.findByUsername(username);
        if(user==null){
            result.put("status",300);
            result.put("mg","账号不存在");
        }else if(!user.getPassword().equals(password)){
            result.put("status",400);
            result.put("mg","密码错误");
        }else {
//            账号和密码验证正确,生成新token或者更新token并保存到数据库中
            result = shiroLoginService.createToken(user.getUserId());
            result.put("status",200);
            result.put("mg","检验通过,登录成功");
        }

        return result;
    }

}

ShiroBaseController

用于用户个人权限和对应操作权限的判断

package com.mycsdnshiro.mycsdnshiro.controller;


import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;


@RestController
@RequestMapping("base")
public class ShiroBaseController {

    @RequiresRoles({"admin"}) //没有的话 AuthorizationException
    @GetMapping("/admin")
    public Map admin(@RequestHeader("token")String token) {
        Map map = new HashMap();
        map.put("status", 200);
        map.put("msg", "当前用户有admin角色");
        return map;
    }
    @RequiresRoles({"teacher"}) //没有的话 AuthorizationException
    @GetMapping("/teacher")
    public Map teacher(@RequestHeader("token")String token) {
        Map map = new HashMap();
        map.put("status", 200);
        map.put("msg", "当前用户有teacher角色");
        return map;
    }
    @RequiresRoles({"student"}) //没有的话 AuthorizationException
    @GetMapping("/student")
    public Map p(@RequestHeader("token")String token) {
        Map map = new HashMap();
        map.put("status", 200);
        map.put("msg", "当前用户有student角色");
        return map;
    }

    @RequiresPermissions({"insert"}) //没有的话 AuthorizationException
    @PostMapping("insert")
    public Map save(@RequestHeader("token") String token) {
        System.out.println("insert");
        Map map = new HashMap();
        map.put("status", 200);
        map.put("msg", "当前用户有insert的权力");
        return map;
    }

    @RequiresPermissions({"delete"}) //没有的话 AuthorizationException
    @DeleteMapping("delete")
    public Map delete(@RequestHeader("token") String token) {
        Map map = new HashMap();
        map.put("status", 200);
        map.put("msg", "当前用户有delete的权力");
        return map;
    }

    @RequiresPermissions({"update"}) //没有的话 AuthorizationException
    @PutMapping("update")
    public Map update(@RequestHeader("token") String token) {
        Map map = new HashMap();
        map.put("status", 200);
        map.put("msg", "当前用户有update的权力");
        return map;
    }

    @RequiresPermissions({"select"}) //没有的话 AuthorizationException
    @GetMapping("select")
    public Map select(@RequestHeader("token") String token) {
        Map map = new HashMap();
        map.put("status", 200);
        map.put("msg", "当前用户有select的权力");
        return map;
    }

}

config

ShiroConfig配置文件

package com.mycsdnshiro.mycsdnshiro.config;


import com.mycsdnshiro.mycsdnshiro.auth.AuthFilter;
import com.mycsdnshiro.mycsdnshiro.auth.AuthRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    @Bean("securityManager")
    public SecurityManager securityManager(AuthRealm authRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(authRealm);
        securityManager.setRememberMeManager(null);
        return securityManager;
    }


    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shifoFilter(SecurityManager securityManager) {

        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        //auth过滤
        Map filters = new HashMap<>();
        filters.put("auth", new AuthFilter());
        shiroFilter.setFilters(filters);
        LinkedHashMap filterMap = new LinkedHashMap<>();
//        auno 匿名访问  auth验证
        filterMap.put("/webjars/**", "anon");
        filterMap.put("/druid/**", "anon");
        filterMap.put("/sys/login", "anon");
        filterMap.put("/swagger/**", "anon");
        filterMap.put("/v2/api-docs", "anon");
        filterMap.put("/swagger-ui.html", "anon");
        filterMap.put("/swagger-resources/**", "anon");
        filterMap.put("/doc.html", "anon");
//        下面这个是放行这个接口(不进行权限判断),因为我们登录需要请求这个接口
        filterMap.put("/user/login", "anon");

//除了以上,其他都需要权限验证
        filterMap.put("/**", "auth");
        shiroFilter.setFilterChainDefinitionMap(filterMap);
        return shiroFilter;


    }

    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }


    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }


}

auth

用户权限判断的一些类

AuthFilter

package com.mycsdnshiro.mycsdnshiro.auth;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.mycsdnshiro.mycsdnshiro.utils.HttpContextUtil;
import com.mycsdnshiro.mycsdnshiro.utils.TokenUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;


@Component
public class AuthFilter extends AuthenticatingFilter {


    // 定义jackson对象
    private static final ObjectMapper MAPPER = new ObjectMapper();

    /**
     * 生成自定义token
     *
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
        //获取请求token
        String token = TokenUtil.getRequestToken((HttpServletRequest) request);
        System.out.println("后端获取前端headers或者参数处的token="+token);

        return new AuthToken(token);
    }

    /**
     * 步骤1.所有请求全部拒绝访问
     *
     * @param request
     * @param response
     * @param mappedValue
     * @return
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if (((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())) {
            return true;
        }
        return false;
    }

    /**
     * 步骤2,拒绝访问的请求,会调用onAccessDenied方法,onAccessDenied方法先获取 token,再调用executeLogin方法
     *
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        //获取请求token,如果token不存在,直接返回
        String token = TokenUtil.getRequestToken((HttpServletRequest) request);
        System.out.println("前端请求token="+token);
        if (StringUtils.isBlank(token)) {
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
            httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtil.getOrigin());
            httpResponse.setCharacterEncoding("UTF-8");
            Map result = new HashMap<>();
            result.put("status", 403);
            result.put("msg", "请先登录");
            String json = MAPPER.writeValueAsString(result);
            httpResponse.getWriter().print(json);
            return false;
        }
        return executeLogin(request, response);
    }

    /**
     * token失效时候调用
     */
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setContentType("application/json;charset=utf-8");
        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtil.getOrigin());
        httpResponse.setCharacterEncoding("UTF-8");
        try {
            //处理登录失败的异常
            Throwable throwable = e.getCause() == null ? e : e.getCause();
            Map result = new HashMap<>();
            result.put("status", 403);
            result.put("msg", "登录凭证已失效,请重新登录");
            String json = MAPPER.writeValueAsString(result);
            httpResponse.getWriter().print(json);
        } catch (IOException e1) {
        }
        return false;
    }

}

AuthToken

package com.mycsdnshiro.mycsdnshiro.auth;


import org.apache.shiro.authc.UsernamePasswordToken;


public class AuthToken extends UsernamePasswordToken {

    private String token;

    public AuthToken(String token) {
        this.token = token;
    }

    @Override
    public Object getPrincipal() {
        return token;
    }

    @Override
    public Object getCredentials() {
        return token;
    }
}

TokenGenerator

生成Token,其中搭配utils工具包使用,在下文

package com.mycsdnshiro.mycsdnshiro.auth;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

public class TokenGenerator {

//    token生成器

    public static String generateValue() {
      return generateValue(UUID.randomUUID().toString());
    }


    private static final char[] hexCode = "0123456789abcdefgh".toCharArray();

    public static String toHexString(byte[] data){
        if (data==null){
            return null;
        }
        StringBuffer r = new StringBuffer(data.length * 2);
        for (byte b: data){
            r.append(hexCode[(b >> 4) & 0xF]);
            r.append(hexCode[(b & 0xF)]);
        }
        return r.toString();
    }



//    生成Token

    public static String generateValue(String param){
        try {
            MessageDigest algorithm = MessageDigest.getInstance("MD5");
            algorithm.reset();
            algorithm.update(param.getBytes());
            byte[] digest = algorithm.digest();
            return toHexString(digest);

        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("生成Token失败");
        }
    }


}

AuthRealm

权限判断入口,登录的时候只会走 doGetAuthenticationInfo,权限判断的时候才会两个都走

package com.mycsdnshiro.mycsdnshiro.auth;



import com.mycsdnshiro.mycsdnshiro.entity.RoleAndPermission;
import com.mycsdnshiro.mycsdnshiro.entity.User;
import com.mycsdnshiro.mycsdnshiro.entity.UserToken;
import com.mycsdnshiro.mycsdnshiro.service.ShiroLoginService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.List;

@Component
public class AuthRealm  extends AuthorizingRealm {

    @Autowired
    private ShiroLoginService shiroLoginService;


//    授权 获取用户角色和权限
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//        1.从 PrincipalCollection 中获取登录用户的信息
        User user = (User) principals.getPrimaryPrincipal();
        List users = shiroLoginService.rolePermissionByRoleId(user.getRoleId());
//        2.添加角色和权限
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//
        Iterator it = users.iterator();
        while (it.hasNext()){
            RoleAndPermission u=it.next();
            simpleAuthorizationInfo.addRole(u.getRoleName());
            simpleAuthorizationInfo.addStringPermission(u.getPermission());
            System.out.println(u.getPermission());
        }


        return simpleAuthorizationInfo;
    }



//    认证 判断token的有效性
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//       获取token,即前端传入的token
        String accessToken=(String)token.getPrincipal();
        System.out.println("此处判断token有效性"+accessToken);

//        1.根据accessToken,查询用户信息(token里面就是前端传来的用户名和密码封装后的)findByToken
        UserToken tokenEntity = shiroLoginService.findByToken(accessToken);
        System.out.println("得到用户实体"+tokenEntity);

//        2.token失败
        if(tokenEntity==null||tokenEntity.getExpireTime().isBefore(LocalDateTime.now())){

            throw new IncorrectCredentialsException("token,请重新登录");
        }

//        3.调用数据库的方法,从数据库中查询 username 对应的用户记录
        User user = shiroLoginService.findByUserId(tokenEntity.getUserId());

//        4.若用户不存在,则异常
        if(user==null){
            throw new UnknownAccountException("用户不存在");
        }

//        5.根据用户的情况,来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, accessToken, this.getName());


        return info;
    }


}

utils

工具类

TokenUtil

获取前端请求体中的token

package com.mycsdnshiro.mycsdnshiro.utils;

import org.apache.commons.lang.StringUtils;

import javax.servlet.http.HttpServletRequest;

/**
 * token工具类
 */
public class TokenUtil {
    /**
     * 获取请求的token
     */
    public static String getRequestToken(HttpServletRequest httpRequest) {

        //从header中获取token
        String token = httpRequest.getHeader("token");
        //如果header中不存在token,则从参数中获取token
        if (StringUtils.isBlank(token)) {
            token = httpRequest.getParameter("token");
        }
        return token;
    }
}

HttpContextUtil

package com.mycsdnshiro.mycsdnshiro.utils;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

public class HttpContextUtil {
    public static HttpServletRequest getHttpServletRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

    public static String getDomain(){
        HttpServletRequest request = getHttpServletRequest();
        StringBuffer url = request.getRequestURL();
        return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
    }

    public static String getOrigin(){
        HttpServletRequest request = getHttpServletRequest();
        return request.getHeader("Origin");
    }
}

hander

全局异常处理类

package com.mycsdnshiro.mycsdnshiro.handler;

import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

/**
 * 全局异常处理器
 */
@RestControllerAdvice
public class MyExceptionHandler {


    @ExceptionHandler(value = AuthorizationException.class)
    public Map handleException(AuthorizationException e) {
        //e.printStackTrace();
        Map result = new HashMap();
        result.put("status", "400");
        //获取错误中中括号的内容
        String message = e.getMessage();
        String msg=message.substring(message.indexOf("[")+1,message.indexOf("]"));
        //判断是角色错误还是权限错误
        if (message.contains("role")) {
            result.put("msg", "对不起,您没有" + msg + "角色");
        } else if (message.contains("permission")) {
            result.put("msg", "对不起,您没有" + msg + "权限");
        } else {
            result.put("msg", "对不起,您的权限有误");
        }
        return result;
    }
}

启动类 

前面没写mapper在这记得扫描

package com.mycsdnshiro.mycsdnshiro;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.mycsdnshiro.mycsdnshiro.mapper")
public class MycsdnshiroApplication {

    public static void main(String[] args) {
        SpringApplication.run(MycsdnshiroApplication.class, args);
    }

}

 到此后端代码结束!!!

前端项目构建

创建项目

照着即可,大伙应该都会

基于Shiro+Springboot+Mybatis+Vue权限管理系统(精)_第15张图片

至于前端页面样式啥的我随便写写就好,主要是咋用token

再npm install

npm  run dev 运行

再加入less 

npm install --save-dev [email protected]

element ui

 npm install --save element-ui

axios

npm install --save axios

前端具体实现

目录如下

基于Shiro+Springboot+Mybatis+Vue权限管理系统(精)_第16张图片

config/index.js

跨域处理

'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.

const path = require('path')

module.exports = {
  dev: {

    // Paths
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',

    // 只改这里---------------前端处理跨域就好了

    proxyTable: {
      '/api': {
        target: 'http://127.0.0.1:9000',
        pathRewrite: {
          '^/api': ''
        }
      }

    },

    // --------------end

    
    // Various Dev Server settings
    host: 'localhost', // can be overwritten by process.env.HOST
    port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
    autoOpenBrowser: false,
    errorOverlay: true,
    notifyOnErrors: true,
    poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-


    
    devtool: 'cheap-module-eval-source-map',


    cacheBusting: true,

    cssSourceMap: true
  },

  build: {
    // Template for index.html
    index: path.resolve(__dirname, '../dist/index.html'),

    // Paths
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',

    productionSourceMap: true,
    devtool: '#source-map',

    productionGzip: false,
    productionGzipExtensions: ['js', 'css'],


    bundleAnalyzerReport: process.env.npm_config_report
  }
}

main.js router.js token.js 

main

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import token from './token/token.js'
import API from './api'

Vue.config.productionTip = false
Vue.use(ElementUI)
Vue.prototype.$API=API
Vue.prototype.getToken = token.isLoginToken
Vue.prototype.setToken = token.setToken
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: ''
})

router.js

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'welcome',
      component: ()=>import("@/views/Welcome"),
      redirect:'login',
      children:[
        {
          path:'login',
          name:'login',
          component:()=>import("@/views/Login")
        },
        {
          path:'home',
          name:'home',
          component:()=>import("@/views/Home")
        },
        {
          path:'adminManage',
          name:'adminManage',
          component:()=>import("@/views/AdminManage")
        },
        {
          path:'studentManage',
          name:'studentManage',
          component:()=>import("@/views/StudentManage")
        },
        {
          path:'teacherManage',
          name:'teacherManage',
          component:()=>import("@/views/TeacherManage")
        },
      ]
    }
  ]
})

token.js

import vue from 'vue'
import router from '../router'
import ElementUI from 'element-ui'
vue.use(ElementUI)
import { Message } from 'element-ui';

const isLoginToken = () => {
    const token = localStorage.getItem("token");
    if (!token) {
        Message({
            showClose: true,
            message: "请先登录",
            type: "error",
            duration: "3000"
        });
        router.push({ path: "/login" });

    }
    return token;
}

const setToken = (token) => {
    localStorage.setItem("token", token);
}


export default {
    isLoginToken,
    setToken
}

api

login.js

import axios from "axios";
const login = (loginInfo) => {
    console.log(loginInfo);
    return axios.post('api/user/login', {
            ...loginInfo
    })
}


export default {
    login,
}

base.js

import axios from 'axios'

const goAdmin = (token) => {
    return axios.get('api/base/admin', {
        headers: {
            token: token
        }
    })
}
const goTeacher = (token) => {
    return axios.get('api/base/teacher', {
        headers: {
            token: token
        }
    })
}
const goStudent = (token) => {
    return axios.get('api/base/student', {
        headers: {
            token: token
        }
    })
}

const goInsert = (token) => {
    return axios.post('/api/base/insert', {}, {
        headers:{
            token:token
        }
    })
}



const goUpdate = (token) => {
    return axios.put('api/base/update', {}, {
        headers:{
            token:token
        }
    })
}

const goDelete = (token) => {
    return axios.delete('api/base/delete', {
        headers: {
            token: token
        }
    })
}
const goSelect = (token) => {
    console.log(token);
    return axios.get('api/base/select', {
        headers: {
            token: token
        }
    })
}
export default {
    goAdmin,
    goTeacher,
    goStudent,
    goInsert,
    goDelete,
    goSelect,
    goUpdate
}

index.js

import login from '@/api/login/login.js'
import base from '@/api/base/base.js'


export default{
    login,
    base
}

views 

Welcome.vue





Login.vue




Home.vue

权限检验页面





流程思路

 本文章参考:这位博主使用的是Jpa一些和其它的技术,我是用了mybatis并进行一些修改

一看就懂!Springboot +Shiro +VUE 前后端分离式权限管理系统_大誌的博客-CSDN博客_shiro前后端分离

你可能感兴趣的:(综合项目,spring,boot,vue.js,java)