SpringBoot + Shiro + Mybatis-plus + Kaptcha + vue实现权限管理、动态路由菜单、登录功能

登录功能

在这里插入图片描述

使用到的技术

  • shiro
  • Mybatis-plus
  • Springboot
  • kaptcha

参考优秀博文

一个博主做的shiro笔记:https://www.guitu18.com/post/2019/07/26/43.html

引入依赖

	<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.7.0</version>
        </dependency>

        <!--mybatis-plus 持久层-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>

        <!--   整合swagger     -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.8.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.8.0</version>
        </dependency>
        <!-- kaptcha 验证码 -->
        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>
    </dependencies>

配置shiro

package com.unclebb.zlgl.config;

import com.unclebb.zlgl.utils.CustomRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**
 * @program: zlgl
 * @description: Shiro配置类:将SecurityManager以及Realm都注入到Spring容器中
 * @author: LiuZhiliang
 * @create: 2021-05-10 08:56
 **/
@Configuration
public class ShiroConfig {
    
    
	
	/** 
    * @Description: 代理生成器,需要借助SpringAOP来扫描@RequiresRoles和@RequiresPermissions等注解。生成代理类实现功能增强,从而实现权限控制。需要配合AuthorizationAttributeSourceAdvisor一起使用,否则权限注解无效。
    * @Param:
    * @return:
    * @Author: Liuzhiliang
    * @Date:
    */ 
    @Bean
    public DefaultAdvisorAutoProxyCreator lifecycleBeanProcessor(){
    
    
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    /** 
    * @Description:  上面配置的DefaultAdvisorAutoProxyCreator相当于一个切面,下面这个类就相当于切点了,两个一起才能实现注解权限控制。
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ 
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager){
    
    
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    /**
     * @Description: Filter工厂,设置对应的过滤条件和跳转条件
     * @Param:
     * @return:
     * @Author: Liuzhiliang
     * @Date:
     */

    //权限管理,配置主要是Realm的管理认证
    @Bean
    public DefaultWebSecurityManager securityManager(){
    
    
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        ThreadContext.bind(securityManager);
        return securityManager;
    }
    //将自己的验证方式加入容器
    @Bean
    public CustomRealm myShiroRealm() {
    
    
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        //加密
        matcher.setHashAlgorithmName("md5");
        matcher.setHashIterations(1);
        CustomRealm customRealm = new CustomRealm();
        customRealm.setCredentialsMatcher(matcher);
        return customRealm;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultSecurityManager securityManager){
    
    
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
//        Map<String,String> maps = new HashMap<>();
//        maps.put("/logout","logout");
//        maps.put("/**","authc");
//        shiroFilterFactoryBean.setLoginUrl("/login");
//        shiroFilterFactoryBean.setUnauthorizedUrl("/403.html");
//        shiroFilterFactoryBean.setSuccessUrl("/index");
//        shiroFilterFactoryBean.setFilterChainDefinitionMap(maps);
        return shiroFilterFactoryBean;
    }

}

配置swagger

package com.unclebb.zlgl.config;

import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * @Author unclebb
 * @Description Swagger配置类
 * @Date 2021/5/9
 **/
@Configuration
@EnableSwagger2
public class Swagger2Config {
    
    

    @Bean
    public Docket webApiConfig(){
    
    

        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .select()
                //过滤掉admin路径下的所有页面
                .paths(Predicates.and(PathSelectors.regex("/user/.*")))
                //过滤掉所有error或error.*页面
                //.paths(Predicates.not(PathSelectors.regex("/error.*")))
                .build();

    }

    private ApiInfo webApiInfo(){
    
    

        return new ApiInfoBuilder()
                .title("网站-API文档")
                .description("本文档描述了网站微服务接口定义")
                .version("1.0")
                .contact(new Contact("qy", "http://atguigu.com", "[email protected]"))
                .build();
    }

}

配置kaptcha

package com.unclebb.zlgl.config;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.Properties;

/**
 * @program: zlgl
 * @description: Kaptcha配置类
 * @author: LiuZhiliang
 * @create: 2021-05-12 09:03
 **/
@Component
public class KaptchaConfig {
    
    
    @Bean
    public DefaultKaptcha getKaptcha(){
    
    
        DefaultKaptcha dk = new DefaultKaptcha();
        Properties properties = new Properties();
        // 图片边框
        properties.setProperty("kaptcha.border", "yes");
        // 边框颜色
        properties.setProperty("kaptcha.border.color", "105,179,90");
        // 字体颜色
        properties.setProperty("kaptcha.textproducer.font.color", "red");
        // 图片宽
        properties.setProperty("kaptcha.image.width", "110");
        // 图片高
        properties.setProperty("kaptcha.image.height", "40");
        // 字体大小
        properties.setProperty("kaptcha.textproducer.font.size", "30");
        // session key
        properties.setProperty("kaptcha.session.key", "code");
        // 验证码长度
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        // 字体
        properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
        Config config = new Config(properties);
        dk.setConfig(config);

        return dk;
    }
}

pojo

User

package com.unclebb.zlgl.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.util.Set;

/**
 * @Author unclebb
 * @Description 用户实体类
 * @Date 2021/5/9
 **/
@Data
@TableName(value = "user")
public class User {
    
    
    @TableId(value = "id",type = IdType.AUTO)//指定自增策略
    private int id;
    @TableField(value = "username")
    private String username;
    @TableField(value = "password")
    private String password;
    @TableField(exist = false)
    private Set<Role> rolesSet;
    private String salt;
}

Role

package com.unclebb.zlgl.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;

import java.util.Set;

/**
 * @Author unclebb
 * @Description 角色实体类
 * @Date 2021/5/9
 **/
@Data
public class Role {
    
    
    @TableId(value = "id",type = IdType.AUTO)//指定自增策略
    private int id;
    @TableField(value = "user_name")
    private String userName;
    @TableField(value = "role_name")
    private String roleName;
    @TableField(exist = false)
    private Set<Permission> permissionSet;
}

Permission

package com.unclebb.zlgl.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;

/**
 * @Author unclebb
 * @Description 权限实体类
 * @Date 2021/5/9
 **/
@Data
public class Permission {
    
    
    @TableId(value = "id",type = IdType.AUTO)//指定自增策略
    private int id;
    @TableField(value = "role_name")
    private String roleName;
    @TableField(value = "permission_name")
    private String permissionName;
}

Mapper

基本格式:

package com.unclebb.zlgl.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.unclebb.zlgl.pojo.User;
import org.apache.ibatis.annotations.Mapper;

/**
 * @Author unclebb
 * @Description 用户映射
 * @Date 2021/5/9
 **/
@Mapper
public interface UserMapper extends BaseMapper<User> {
    
    
}

Service

Interface

LoginService

package com.unclebb.zlgl.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.unclebb.zlgl.pojo.User;
import org.springframework.stereotype.Service;

/**
 * @Author unclebb
 * @Description 用户登录接口
 * @Date 2021/5/9
 **/
@Service
public interface LoginService extends IService<User> {
    
    
    public User getByUsername(String username);
}

RoleService

package com.unclebb.zlgl.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.unclebb.zlgl.pojo.Role;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public interface RoleService extends IService<Role> {
    
    
    public List<Role> getRole(String userName);
}

PermissionService

package com.unclebb.zlgl.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.unclebb.zlgl.pojo.Permission;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public interface PermissionService extends IService<Permission> {
    
    
    public List<Permission> getPermissions(String roleName);
}

ServiceImpl

LoginServiceImpl

package com.unclebb.zlgl.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.unclebb.zlgl.mapper.UserMapper;
import com.unclebb.zlgl.pojo.Role;
import com.unclebb.zlgl.pojo.User;
import com.unclebb.zlgl.service.LoginService;
import com.unclebb.zlgl.service.PermissionService;
import com.unclebb.zlgl.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @Author unclebb
 * @Description 登录实现类
 * @Date 2021/5/9
 **/
@Service
public class LoginServiceImpl extends ServiceImpl<UserMapper, User> implements LoginService {
    
    

    @Autowired
    RoleService roleService;

    @Override
    public User getByUsername(String username) {
    
    
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("username",username);
        User user = this.getOne(wrapper);
        List<Role> roleList = roleService.getRole(user.getUsername());
        user.setRolesSet(new HashSet<Role>(roleList));
        return user;
    }
}

RoleServiceImpl

package com.unclebb.zlgl.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.unclebb.zlgl.mapper.RoleMapper;
import com.unclebb.zlgl.pojo.Permission;
import com.unclebb.zlgl.pojo.Role;
import com.unclebb.zlgl.service.PermissionService;
import com.unclebb.zlgl.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @program: zlgl
 * @description: Role实现类
 * @author: LiuZhiliang
 * @create: 2021-05-10 16:16
 **/
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {
    
    
    @Autowired
    PermissionService permissionService;
    @Override
    public List<Role> getRole(String userName) {
    
    
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("user_name",userName);
        List<Role> roleList = this.list(wrapper);
        for (Role role:roleList){
    
    
            List<Permission> permissions = permissionService.getPermissions(role.getRoleName());
            role.setPermissionSet(new HashSet<Permission>(permissions));
        }
        return roleList;
    }
}

PermissionServiceImpl

package com.unclebb.zlgl.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.unclebb.zlgl.mapper.PermissionMapper;
import com.unclebb.zlgl.pojo.Permission;
import com.unclebb.zlgl.pojo.User;
import com.unclebb.zlgl.service.PermissionService;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;

/**
 * @program: zlgl
 * @description: Permission实现类
 * @author: LiuZhiliang
 * @create: 2021-05-10 16:18
 **/
@Service
public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permission> implements PermissionService {
    
    
    @Override
    public List<Permission> getPermissions(String roleName) {
    
    
        QueryWrapper<Permission> wrapper = new QueryWrapper<>();
        wrapper.eq("role_name",roleName);
        return this.list(wrapper);
    }
}

Controller

package com.unclebb.zlgl.controller;

import com.unclebb.zlgl.pojo.Role;
import com.unclebb.zlgl.pojo.User;
import com.unclebb.zlgl.service.RoleService;
import com.unclebb.zlgl.utils.Result;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpSession;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author unclebb
 * @Description 登录控制器
 * @Date 2021/5/9
 **/
@RestController
public class LoginController {
    
    

    @Autowired
    RoleService roleService;

    /** 
    * @Description: 登录接口 
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ 
    @ApiOperation(value = "登录接口")
    @RequestMapping("/user/login")
    public Result login(@RequestBody User user){
    
    
        System.out.println("进入/user/login API接口");
        if (StringUtils.isEmpty(user.getUsername())||StringUtils.isEmpty(user.getPassword())){
    
    
            return Result.fail("请输入用户名密码");
        }
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        Object sessionId = session.getId();
        Map ret = new HashMap<String,Object>();
        ret.put("token",sessionId);
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
        try {
    
    
            //登录成功
            subject.login(token);
            return Result.ok(ret);
        } catch (AuthenticationException e) {
    
    
            e.printStackTrace();
            //登录失败
            return Result.fail("用户名密码错误");
        }
    }

    /** 
    * @Description: 基本测试接口 
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ 
    @RequestMapping("/user/noauthority")
    public Result noauthority(){
    
    
        return Result.fail("没有权限");
    }

    
    /** 
    * @Description: 测试session接口 
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ 
    @RequestMapping("/user/demoSession")
    @ResponseBody
    public String demoSession(HttpSession session){
    
    
        System.out.println("测试session");
        Enumeration<String> names = session.getAttributeNames();
        while (names.hasMoreElements()){
    
    
            String name = names.nextElement();
            Object value = session.getAttribute(name);
            System.out.println(name + " -------  "+ value);
        }
        return "session 取值";
    }

    /** 
    * @Description: 测试session接口 
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ 
    @RequestMapping("/user/demoSession2")
    @ResponseBody
    public String demoSession2(){
    
    
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        System.out.println(session.getHost());
        System.out.println(session.getId());
        System.out.println(session.getLastAccessTime().getTime());
        System.out.println(session.getTimeout());
        System.out.println(session.getAttribute("test"));
        return "session 取值";
    }

    /** 
    * @Description: 测试session接口 
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ 
    @RequestMapping("/user/checkPermission")
    @ResponseBody
    public Result checkPermission(User user){
    
    
        if (StringUtils.isEmpty(user.getUsername())||StringUtils.isEmpty(user.getPassword())){
    
    
            return Result.fail("请输入用户名密码");
        }
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        session.setAttribute("test","test");
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
        token.setRememberMe(true);
        try {
    
    
            //登录成功
            subject.login(token);
        } catch (AuthenticationException e) {
    
    
            e.printStackTrace();
            //登录失败
            return Result.fail("用户名密码错误");
        }
        try {
    
    
            subject.checkRole("admin");
            return Result.ok("权限检查成功");
        } catch (AuthorizationException e) {
    
    
            e.printStackTrace();
            return Result.fail("检查权限失败");
        }
    }

    /** 
    * @Description: 根据token获取用户授权信息接口 
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ 
    @RequestMapping("/user/checkRole")
    public Result checkRole(@RequestParam String token){
    
    
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        List<Role> roleList = null;
        if (token.equals(session.getId().toString())){
    
    
            String username = session.getAttribute("org.apache.shiro.subject.support.DefaultSubjectContext_PRINCIPALS_SESSION_KEY").toString();
            System.out.println(username);
            roleList = roleService.getRole(username);
        }
        return Result.ok(roleList);
    }
    
    /** 
    * @Description: 测试kaptcha ,获取验证码
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ 
    @RequestMapping("/user/testKaptcha")
    @ResponseBody
    public String TestKaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
    
    
        byte[] captcha = null;
        ByteArrayOutputStream out = new ByteArrayOutputStream();

        try {
    
    
            // 将生成的验证码保存在session中
            String createText = defaultKaptcha.createText();
            request.getSession().setAttribute("rightCode", createText);
            BufferedImage bi = defaultKaptcha.createImage(createText);
            ImageIO.write(bi, "jpg", out);
        } catch (Exception e) {
    
    
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
        }

        captcha = out.toByteArray();
        response.setHeader("Cache-Control", "no-store");
        response.setHeader("Pragma", "no-cache");
        response.setDateHeader("Expires", 0);
        response.setContentType("image/jpeg");
        ServletOutputStream sout = response.getOutputStream();
        sout.write(captcha);
        sout.flush();
        sout.close();
        return "测试Kaptcha";
    }

    /**
     * 校对验证码
     *
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(value = "/user/verifyKaptcha")
    public Result imgvrifyControllerDefaultKaptcha(HttpServletRequest request, HttpServletResponse response) {
    
    
        ModelAndView model = new ModelAndView();
        String rightCode = (String) request.getSession().getAttribute("rightCode");
        String tryCode = request.getParameter("tryCode");
        System.out.println("rightCode:" + rightCode + " ———— tryCode:" + tryCode);
        if (!rightCode.equals(tryCode)) {
    
    
            model.addObject("info", "验证码错误,请再输一次!");
            model.setViewName("login");
            return Result.fail("验证码错误");
        } else {
    
    
            model.addObject("info", "登陆成功");
            model.setViewName("index");
            return Result.ok("验证成功");
        }
    }

 	/** 
    * @Description: 测试 校验权限 permission 
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ 
    @RequestMapping("/user/testShiroPermission")
    @RequiresPermissions("user:add")
    public Result TestShiroPermissions(){
    
    
        System.out.println("访问 TestShiroPermissions API");
        Subject subject = SecurityUtils.getSubject();
        String username = (String) subject.getPrincipal();
        System.out.println(username);
        return Result.ok(username);
    }

    /** 
    * @Description: 登出功能API
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ 
    @RequestMapping("/user/logout")
    public Result logout(){
    
    
        Subject subject = SecurityUtils.getSubject();
        String username = (String) subject.getPrincipal();
        subject.logout();
        return Result.ok(username);
    }
    /** 
    * @Description:  测试校验 角色 role
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ 
    @RequestMapping("/user/TestRole")
    @RequiresRoles("admin")
    public Result TestRole(){
    
    
        System.out.println("测试TestRole");
        return Result.ok("测试Role");
    }

}

util

Result

package com.unclebb.zlgl.utils;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
 * @Author unclebb
 * @Description 统一返回API格式
 * @Date 2021/5/9
 **/
@Data
@ApiModel(value = "全局统一返回结果")
public class Result<T> {
    
    

    @ApiModelProperty(value = "返回码")
    private Integer code;

    @ApiModelProperty(value = "返回消息")
    private String message;

    @ApiModelProperty(value = "返回数据")
    private T data;

    public Result(){
    
    }

    public static <T> Result<T> build(T data) {
    
    
        Result<T> result = new Result<T>();
        if (data != null)
            result.setData(data);
        return result;
    }

    public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {
    
    
        Result<T> result = build(body);
        result.setCode(resultCodeEnum.getCode());
        result.setMessage(resultCodeEnum.getMessage());
        return result;
    }

    public static<T> Result<T> ok(){
    
    
        return Result.ok(null);
    }

    /**
     * 操作成功
     * @param data
     * @param <T>
     * @return
     */
    public static<T> Result<T> ok(T data){
    
    
        Result<T> result = build(data);
        return build(data, ResultCodeEnum.SUCCESS);
    }

    public static<T> Result<T> fail(){
    
    
        return Result.fail(null);
    }

    /**
     * 操作失败
     * @param data
     * @param <T>
     * @return
     */
    public static<T> Result<T> fail(T data){
    
    
        Result<T> result = build(data);
        return build(data, ResultCodeEnum.FAIL);
    }

    public Result<T> message(String msg){
    
    
        this.setMessage(msg);
        return this;
    }

    public Result<T> code(Integer code){
    
    
        this.setCode(code);
        return this;
    }
}

ResultCode

package com.unclebb.zlgl.utils;

import lombok.Getter;

/**
 * @Author unclebb
 * @Description API状态信息
 * @Date 2021/5/9
 **/
@Getter
public enum ResultCodeEnum {
    
    

    SUCCESS(200,"成功"),
    FAIL(201, "失败"),
    SERVICE_ERROR(202, "服务异常"),
    DATA_ERROR(204, "数据异常"),

    SIGN_ERROR(300, "签名错误"),

    PAY_PASSWORD_ERROR(401, "支付密码错误"),
    REPEAT_ERROR(402, "重复提交"),

    INVEST_AMMOUNT_MORE_ERROR(501, "出借金额已经多余标的金额"),
    RETURN_AMMOUNT_MORE_ERROR(502, "还款金额不正确"),
    PROJECT_AMMOUNT_ERROR(503, "标的金额不一致")
    ;

    private Integer code;

    private String message;

    private ResultCodeEnum(Integer code, String message) {
    
    
        this.code = code;
        this.message = message;
    }
}

CustomRealm

package com.unclebb.zlgl.utils;

import com.unclebb.zlgl.pojo.Permission;
import com.unclebb.zlgl.pojo.Role;
import com.unclebb.zlgl.pojo.User;
import com.unclebb.zlgl.service.LoginService;
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.authz.SimpleAuthorizationInfo;
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;

/**
 * @program: zlgl
 * @description: 自定义Realm
 * @author: LiuZhiliang
 * @create: 2021-05-10 09:09
 **/
public class CustomRealm extends AuthorizingRealm {
    
    

    @Autowired
    LoginService loginService;

    /** 
    * @Description: 授权配置
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ 
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    
    
        String username = (String) principals.getPrimaryPrincipal();
        User user = loginService.getByUsername(username);
        if (user == null){
    
    
            return null;
        }else {
    
    
            SimpleAuthorizationInfo  simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            for (Role role : user.getRolesSet()){
    
    
                simpleAuthorizationInfo.addRole(role.getRoleName());
                for (Permission permission : role.getPermissionSet()){
    
    
                    simpleAuthorizationInfo.addStringPermission(permission.getPermissionName());
                }
            }
            return simpleAuthorizationInfo;
        }
    }

    /** 
    * @Description: 认证配置 
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ 
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
    
        String username = token.getPrincipal().toString();
        User user = loginService.getByUsername(username);
        if (user == null){
    
    
            return null;
        }else {
    
    
            //匹配密码
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,user.getPassword(),getName());
            simpleAuthenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(user.getSalt()));
            return simpleAuthenticationInfo;
        }
    }
}

前端代码

前端我这里直接用了 开源的管理系统框架
附地址:

https://github.com/PanJiaChen/vue-admin-template

运行截图如下
在这里插入图片描述
在这里插入图片描述

完事儿只需要改一下它的 返回状态码校验,配置下跨域就可以了
在这里插入图片描述

整合验证码

验证码部分包含两个API接口

/user/testKaptcha 获取验证码信息
/user/verifyKaptcha 校验

其中获取验证码图片信息相当于请求静态图片资源,直接将验证码图片的src指向 该接口即可,前端源码如下:

          <el-image :src="kaptcha" @click="refreshCode()" alt="加载失败" style="margin-left:10px;height:40px;margin-top:5px">
            <div slot="placeholder" class="image-slot">
                <i class="el-icon-loading"></i>
            </div>
          </el-image>

其中路径定义为:

kaptcha:"http://localhost:8082/user/testKaptcha?t="+ new Date().getTime(),

后面加的时间参数是为了刷新url用的
前端的刷新函数就是将kaptcha重新赋值

    refreshCode(){
    
    
      console.log("测试切换验证码")
      this.kaptcha = "http://localhost:8082/user/testKaptcha?t="+ new Date().getTime()
      console.log(this.kaptcha)
    },

登录、权限校验、登出效果如下

登录

登录首先会验证 验证码的正确性,登陆成功进入主界面

验证码错误如下:
在这里插入图片描述
用户名密码错误如下:
在这里插入图片描述
登陆成功后请求校验角色 校验成功
在这里插入图片描述请求校验权限 校验成功
在这里插入图片描述

退出,执行两次,data为null
在这里插入图片描述

再次校验权限,报出异常

在这里插入图片描述

动态路由

参考博文:

https://www.bilibili.com/video/BV1H54y1S7mD?p=2

我把视频中的流程图扒过来,结合代码简单说明一下
在这里插入图片描述

首先进行验证码验证,验证码验证通过后
--------------------------------------------------
验证用户名密码,用户名密码验证通过后,vuex存储token
在这里插入图片描述
看一下处理请求的代码段在这里插入图片描述
--------------------------------------------------
拦截请求,请求校验vuex中是否含有用户名、权限信息,若没有则进行请求接口获取角色信息并生成动态路由菜单
在这里插入图片描述

这里有之前总结的 js求数组交集的代码

var numOne = [0, 2, 4, 6, 8, 8];
var numTwo = [1, 2, 3, 4, 5, 6];
var duplicatedValues = [new Set(numOne)].filter(item => numTwo.includes(item));
console.log(duplicatedValues); // returns [2, 4, 6]

猜你喜欢

转载自blog.csdn.net/lzl980111/article/details/116662930