springboot + shiro + jwt 实现前后端分离

sql脚本

/*
 Navicat Premium Data Transfer

 Source Server         : 127.0.0.1
 Source Server Type    : MySQL
 Source Server Version : 50726
 Source Host           : 127.0.0.1:3306
 Source Schema         : financial

 Target Server Type    : MySQL
 Target Server Version : 50726
 File Encoding         : 65001

 Date: 24/11/2021 15:23:12
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for sys_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_menu`;
CREATE TABLE `sys_menu`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `parent_id` bigint(20) NULL DEFAULT NULL COMMENT '父菜单ID,一级菜单为0',
  `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '菜单名称',
  `url` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '菜单URL',
  `perms` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '授权(多个用逗号分隔,如:user:list,user:create)',
  `type` int(11) NULL DEFAULT NULL COMMENT '类型   1:目录   2:菜单   3:按钮',
  `icon` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '菜单图标',
  `system_type` int(255) NULL DEFAULT NULL COMMENT '系统类型 1:管理员端 2:client 端',
  `order_num` int(11) NULL DEFAULT NULL COMMENT '排序',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 41 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '菜单管理' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `role_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '角色名称',
  `remark` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
  `dept_id` bigint(20) NULL DEFAULT NULL COMMENT '部门ID',
  `company_id` bigint(20) NULL DEFAULT NULL COMMENT '公司id',
  `create_date` datetime(0) NULL DEFAULT NULL COMMENT '创建日期',
  `create_by` bigint(11) NULL DEFAULT NULL COMMENT '创建人',
  `update_date` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
  `update_by` bigint(11) NULL DEFAULT NULL COMMENT '修改人',
  `del_flag` tinyint(1) NULL DEFAULT NULL COMMENT '是否删除 0:否,1:是',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for sys_role_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_menu`;
CREATE TABLE `sys_role_menu`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `role_id` bigint(20) NULL DEFAULT NULL COMMENT '角色ID',
  `menu_id` bigint(20) NULL DEFAULT NULL COMMENT '菜单ID',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色与菜单对应关系' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名',
  `password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '密码',
  `salt` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '盐',
  `email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '邮箱',
  `mobile` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '手机号',
  `status` tinyint(4) NULL DEFAULT NULL COMMENT '状态  0:禁用   1:正常',
  `dept_id` bigint(20) NULL DEFAULT NULL COMMENT '部门ID',
  `company_id` bigint(20) NULL DEFAULT NULL COMMENT '公司id',
  `create_date` datetime(0) NULL DEFAULT NULL COMMENT '创建日期',
  `create_by` bigint(11) NULL DEFAULT NULL COMMENT '创建人',
  `update_date` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
  `update_by` bigint(11) NULL DEFAULT NULL COMMENT '修改人',
  `del_flag` tinyint(1) NULL DEFAULT NULL COMMENT '是否删除 0:否,1:是',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `username`(`username`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统用户' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) NULL DEFAULT NULL COMMENT '用户ID',
  `role_id` bigint(20) NULL DEFAULT NULL COMMENT '角色ID',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户与角色对应关系' ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

依赖

springboot 版本号为 2.5.6
<properties>
        <shiro.version>1.6.0</shiro.version>
        <java-jwt.version>3.2.0</java-jwt.version>
</properties>
<dependencies>
	<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
	<dependency>
	    <groupId>org.apache.shiro</groupId>
	    <artifactId>shiro-spring-boot-starter</artifactId>
	    <version>${shiro.version}</version>
	</dependency>
	<dependency>
	    <groupId>com.auth0</groupId>
	    <artifactId>java-jwt</artifactId>
	    <version>${java-jwt.version}</version>
	</dependency>
</dependencies>

代码

ShiroConfig


package com.sun.financial.common.config;

import com.sun.financial.common.filter.JwtTokenFilter;
import com.sun.financial.common.filter.PermissionAuthFilter;
import com.sun.financial.modules.shiro.realm.UserRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.SessionsSecurityManager;
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.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Shiro的配置文件
 *
 * @author 13027619526@163.com
 */
@Configuration
public class ShiroConfig {

    /**
     * 注入 securityManager
     * @param userRealm
     * @return
     */
    @Bean("securityManager")
    public SessionsSecurityManager securityManager(UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置自定义 realm
        securityManager.setRealm(userRealm);
        return securityManager;
    }


    /**
     * @param securityManager
     * @return
     */
    @Bean("shiroFilterFactoryBean")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {

        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();

        // 添加自己的过滤器并且取名为jwt
        Map<String, Filter> filterMap = new LinkedHashMap<>();
        //设置我们自定义的JWT过滤器
        filterMap.put("jwtToken", new JwtTokenFilter());
        filterMap.put("permission", new PermissionAuthFilter());
        shiroFilter.setFilters(filterMap);

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        //API 文档放行
        filterChainDefinitionMap.put("/doc/index.html","anon");
        filterChainDefinitionMap.put("/doc/AllInOne.css","anon");
        filterChainDefinitionMap.put("/doc/debug.js","anon");
        filterChainDefinitionMap.put("/doc/font.css","anon");
        filterChainDefinitionMap.put("/doc/highlight.min.js","anon");
        filterChainDefinitionMap.put("/doc/jquery.min.js","anon");
        filterChainDefinitionMap.put("/doc/xt256.min.css","anon");
        //不需要检验的接口
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/logout", "anon");
        //除以上之外全部校验
        filterChainDefinitionMap.put("/**", "jwtToken,permission");
        shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);
        shiroFilter.setSecurityManager(securityManager);
        return shiroFilter;
    }

    /**
     * 添加注解支持
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        // 强制使用cglib,防止重复代理和可能引起代理出错的问题
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

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

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

JwtTokenFilter

package com.sun.financial.common.filter;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.sun.financial.common.constant.BaseConstant;
import com.sun.financial.common.utils.JwtUtils;
import com.sun.financial.common.utils.Result;
import com.sun.financial.common.utils.ResultCode;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.springframework.http.HttpStatus;
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;

public class JwtTokenFilter extends AccessControlFilter {

    /**
     *
     * 如果带有 token,则对 token 进行检查,否则直接通过
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws UnauthorizedException {
        HttpServletRequest req = (HttpServletRequest) request;
        String token = req.getHeader(BaseConstant.TOKEN);
        //判断请求的请求头是否带上 "token"
        if (StrUtil.isBlank(token)) {
            return false;
        }
        if (!JwtUtils.verify(token)){
            return false;
        }
        return true;
    }

    /**
     * Processes requests where the subject was denied access as determined by the
     * {@link #isAccessAllowed(ServletRequest, ServletResponse, Object) isAccessAllowed}
     * method.
     *
     * @param request  the incoming <code>ServletRequest</code>
     * @param response the outgoing <code>ServletResponse</code>
     * @return <code>true</code> if the request should continue to be processed; false if the subclass will
     * handle/render the response directly.
     * @throws Exception if there is an error processing the request.
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().print(JSONObject.toJSON(new Result<>(ResultCode.INVALID_TOKEN)));
        return false;
    }



    /**
     * 对跨域提供支持
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }
}

PermissionAuthFilter

package com.sun.financial.common.filter;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.sun.financial.common.config.SpringConfig;
import com.sun.financial.common.constant.BaseConstant;
import com.sun.financial.common.utils.JwtUtils;
import com.sun.financial.common.utils.Result;
import com.sun.financial.common.utils.ResultCode;
import com.sun.financial.modules.sys.entity.SysMenuEntity;
import com.sun.financial.modules.sys.service.SysMenuService;
import org.apache.shiro.web.filter.AccessControlFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;

public class PermissionAuthFilter extends AccessControlFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        boolean flag = true;
        HttpServletRequest req = (HttpServletRequest) request;
        String requestURI = req.getRequestURI();
        System.out.println(requestURI);
        String[] split = requestURI.substring(1).split("/");
        if (split.length>0){
            String token = req.getHeader(BaseConstant.TOKEN);
            Long id = JwtUtils.getUserId(token);
            SysMenuService sysMenuService = SpringConfig.getBean(SysMenuService.class);
            List<SysMenuEntity> userMenuList = sysMenuService.getUserMenuList(id);
            List<String> strings = userMenuList.parallelStream().filter(i -> StrUtil.isNotBlank(i.getPerms()))
                    .distinct().map(SysMenuEntity::getPerms).collect(Collectors.toList());
            //路径格式为 /XX/XX/XX,可根据自己喜好更改
            String concat = split[0].concat(":").concat(split[1].concat(":").concat(split[2]));
            System.out.println(concat);
            if (strings.contains(concat)){
                flag = true;
            }
        }
        return flag;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().print(JSONObject.toJSON(new Result<>(ResultCode.UNAUTHORIZED)));
        return false;
    }
}

JwtUtils

package com.sun.financial.common.utils;

import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sun.financial.common.config.SpringConfig;
import com.sun.financial.common.constant.BaseConstant;
import com.sun.financial.modules.sys.entity.SysUserEntity;

import java.util.Map;


public class JwtUtils {
    // 密钥
    private static final String SECRET = "11111111111111111111";

    // 密钥
    private static final String EXPIRE_TIME = "expire_time";

    /**
     * 生成 token
     */
    public static String createToken(Map<String, Object> map) {
        map.put(EXPIRE_TIME, System.currentTimeMillis() + BaseConstant.EXPIRE_TIME);
        String token = JWTUtil.createToken(map, SECRET.getBytes());
        return token;
    }

    /**
     * 校验 token 是否正确
     */
    public static boolean verify(String token) {
        boolean verify = JWTUtil.verify(token, SECRET.getBytes());
        if (!verify){
            return false;
        }
        JWTPayload jwtPayload = getJWTPayload(token);
        long expireTime = (long)jwtPayload.getClaim(EXPIRE_TIME);
        long timeMillis = System.currentTimeMillis();
        if (timeMillis>expireTime){
            return false;
        }
        RedisUtils redisUtils = SpringConfig.getBean(RedisUtils.class);
        String jwtToken = redisUtils.get(token,String.class);
        if (jwtToken == null){
            return false;
        }
        return verify;
    }

    /**
     * 获得token中的信息,无需secret解密也能获得
     */
    public static JWTPayload getJWTPayload(String token) {
        final JWT jwt = JWTUtil.parseToken(token);
        JWTPayload payload = jwt.getPayload();
        return payload;
    }

    /**
     * 获得token中的信息,无需secret解密也能获得
     */
    public static SysUserEntity getSysUserEntity(String token) {
        JWTPayload jwtPayload = getJWTPayload(token);
        SysUserEntity sysUserEntity = JSONObject.parseObject(JSON.toJSONString(jwtPayload.getClaimsJson()), SysUserEntity.class);
        return sysUserEntity;
    }

    /**
     * 获取登陆账号id
     * @param token
     * @return
     */
    public static Long getUserId(String token) {
        SysUserEntity payload = getSysUserEntity(token);
        Long id = payload.getId();
        return id;
    }

    /**
     * 获取登陆账号id
     * @param token
     * @return
     */
    public static String getSalt(String token) {
        SysUserEntity payload = getSysUserEntity(token);
        String salt = payload.getSalt();
        return salt;
    }

    /**
     * 获取登陆账号 所在公司的id
     * @param token
     * @return
     */
    public static Long getCompanyId(String token) {
        SysUserEntity payload = getSysUserEntity(token);
        Long id = payload.getCompanyId();
        return id;
    }

    /**
     * 获取登陆人username
     * @param token
     * @return
     */
    public static String getUsername(String token) {
        SysUserEntity payload = getSysUserEntity(token);
        String id = payload.getUsername();
        return id;
    }

}

UserRealm

package com.sun.financial.modules.shiro.realm;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sun.financial.common.constant.BaseConstant;
import com.sun.financial.common.utils.ShiroUtils;
import com.sun.financial.modules.sys.mapper.SysMenuMapper;
import com.sun.financial.modules.sys.mapper.SysUserMapper;
import com.sun.financial.modules.sys.entity.SysMenuEntity;
import com.sun.financial.modules.sys.entity.SysUserEntity;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
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;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * 认证
 *
 * @author 13027619526@163.com
 */
@Component
public class UserRealm extends AuthorizingRealm {
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysMenuMapper sysMenuMapper;
    
    
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		
		return null;
	}

	/**
	 * 认证(登录时调用)
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken authcToken) throws AuthenticationException {
		UsernamePasswordToken token = (UsernamePasswordToken)authcToken;

		//查询用户信息
		SysUserEntity user = sysUserMapper.selectOne(new QueryWrapper<SysUserEntity>().lambda().eq(SysUserEntity::getUsername, token.getUsername()));
		//账号不存在
		if(user == null) {
			throw new UnknownAccountException("账号不存在");
		}
		//账号锁定
		if(user.getStatus() == 0){
			throw new LockedAccountException("账号已被锁定,请联系管理员");
		}
		SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), ByteSource.Util.bytes(user.getSalt()), getName());
		return info;
	}

	@Override
	public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
		HashedCredentialsMatcher shaCredentialsMatcher = new HashedCredentialsMatcher();
		shaCredentialsMatcher.setHashAlgorithmName(ShiroUtils.hashAlgorithmName);
		shaCredentialsMatcher.setHashIterations(ShiroUtils.hashIterations);
		super.setCredentialsMatcher(shaCredentialsMatcher);
	}
}

SysLoginController


package com.sun.financial.modules.shiro.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sun.financial.common.annotation.SysLog;
import com.sun.financial.common.constant.BaseConstant;
import com.sun.financial.common.utils.*;
import com.sun.financial.modules.shiro.request.LoginRequest;
import com.sun.financial.modules.sys.entity.SysUserEntity;
import com.sun.financial.modules.sys.service.SysUserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.Map;


/**
 * 登录相关
 *
 * @author 13027619526@163.com
 */
@RestController
public class SysLoginController {


	@Autowired
	private RedisUtils redisUtils;
	@Autowired
	private SysUserService sysUserService;

	/**
	 * 登录
	 * @param request
	 * @return
	 */
	@PostMapping(value = "login")
	public Result login(@Valid @RequestBody LoginRequest request) {
		try{
			Subject subject = ShiroUtils.getSubject();
			UsernamePasswordToken token = new UsernamePasswordToken(request.getUsername(), request.getPassword());
			subject.login(token);
			SysUserEntity user = sysUserService.getOne(new QueryWrapper<SysUserEntity>().lambda().eq(SysUserEntity::getUsername, token.getUsername()));
			String jsonString = JSON.toJSONString(user);
			Map<String,Object> map = JSONObject.parseObject(jsonString);
			map.remove("password");
			String jwtToken = JwtUtils.createToken(map);
			redisUtils.set(jwtToken,jsonString, BaseConstant.EXPIRE_TIME);
			return Result.success(jwtToken);
		}catch (UnknownAccountException e) {
			return Result.error(e.getMessage());
		}catch (IncorrectCredentialsException e) {
			return Result.error("密码不正确");
		}catch (LockedAccountException e) {
			return Result.error(e.getMessage());
		}catch (AuthenticationException e) {
			return Result.error("账号或密码错误!");
		}
	}

	/**
	 * 退出登录
	 * @return
	 */
	@PostMapping(value = "logout")
	public Result logout(@RequestHeader String token) {
		ShiroUtils.logout();
		redisUtils.delete(token);
		return Result.success();
	}
	
}

ShiroUtils


package com.sun.financial.common.utils;

import com.sun.financial.modules.sys.entity.SysUserEntity;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.subject.Subject;

/**
 * Shiro工具类
 *
 * @author 13027619526@163.com
 */
public class ShiroUtils {
	/**  加密算法 */
	public final static String hashAlgorithmName = "SHA-256";
	/**  循环次数 */
	public final static int hashIterations = 16;

	public static String sha256(String password, String salt) {
		return new SimpleHash(hashAlgorithmName, password, salt, hashIterations).toString();
	}

	public static Subject getSubject() {
		return SecurityUtils.getSubject();
	}

	public static SysUserEntity getUserEntity() {
		return (SysUserEntity)SecurityUtils.getSubject().getPrincipal();
	}

	public static void logout() {
		SecurityUtils.getSubject().logout();
	}

}
### 回答1: springboot+shiro+jwt 是一种常见的后端技术组合,其中 springboot 是一个基于 Spring 框架的快速开发框架,shiro 是一个安全框架,用于身份验证、授权和加密等功能,jwt 是一种基于 token 的身份验证机制,可以用于前后端分离的应用中。这种技术组合可以帮助开发者快速搭建安全可靠的后端服务。 ### 回答2: Springboot是一个开源的Java开发框架,是基于Spring框架的远程服务器控制技术方案,是现代化的全栈框架,集成丰富,提供了大量的开箱即用的组件,可以大大简化开发人员的开发工作。 Shiro是一个强大的轻量级安全框架,支持用户身份识别、密码加密、权限控制、会话管理等一系列安全功能,被广泛应用于JavaWeb开发中。 JWT(JSON Web Token)是一种开放标准(RFC 7519),定义了一种简洁的、自包含的方式,用于通信双方之间以JSON对象的形式安全地传递信息。JWT可以用于状态管理和用户身份认证等场景。 在使用SpringBoot开发Web应用过程中,ShiroJWT可以同时用于用户身份认证和权限控制。Shiro提供了一系列的身份识别、密码加密、权限控制、会话管理等功能,而JWT则可以实现无状态的身份认证。使用ShiroJWT,可以有效地保护Web应用的安全,避免被恶意攻击者利用。 具体而言,使用ShiroJWT可以将用户认证的主要逻辑统一在一起,实现更加优雅的认证和授权过程。同时,这样的组合也可以避免一些常见的安全漏洞,比如会话劫持、XSS攻击、CSRF等。 在实际开发中,使用SpringBoot Shiro JWT可以方便地进行二次开发,进一步优化开发成本和提高开发效率。同时,使用这个组合可以让开发者更好地专注于业务逻辑的开发,而无需关注安全问题,从而提高开发质量和开发人员的工作效率。 ### 回答3: Spring Boot是一种基于Spring框架的快速开发微服务的工具,能够使开发者可以快速构建基于Spring的应用程序。而Shiro是一个强大易用的Java安全框架,可用于身份验证、权限控制、加密等。JWT(JSON Web Token)是一种基于JSON的安全令牌,可用于在客户端和服务器之间传递信息。 在使用Spring Boot开发Web应用程序时,通常需要进行用户身份验证和访问控制,这时候就可以使用Shiro实现这些功能。同时,由于Web应用程序需要跨域访问,因此使用JWT可以方便地实现身份验证和授权的管理。 在使用Spring Boot和Shiro时,可以使用JWT作为身份验证和授权的管理工具。此时,需要进行以下几个步骤: 1.添加ShiroJWT的依赖 在Spring Boot项目中,可以通过Maven或Gradle等工具添加ShiroJWT的依赖。例如,可以添加以下依赖: <!-- Shiro依赖 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.7.0</version> </dependency> <!-- JWT依赖 --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> 2.配置Shiro 在Spring Boot项目中,可以通过在application.properties或application.yml文件中进行Shiro的配置。例如,可以配置以下内容: # Shiro配置 shiro: user: loginUrl: /login # 登录页面URL jwt: secret: my_secret # JWT加密密钥 expiration: 1800000 # JWT过期时间,单位为毫秒,默认30分钟 3.创建Shiro的Realm 在Shiro中,Realm是用于从应用程序中获取安全数据(如用户、角色和权限)的组件。因此,需要创建Shiro的Realm,用于管理用户的认证和授权。 例如,可以创建一个自定义的Realm,其中包括从数据库中获取用户和角色的方法: public class MyRealm extends AuthorizingRealm { @Autowired private UserService userService; /** * 认证,验证用户身份 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); User user = userService.findByUsername(username); if (user == null) { throw new UnknownAccountException("用户名或密码错误"); } String password = user.getPassword(); return new SimpleAuthenticationInfo(user.getUsername(), password, getName()); } /** * 授权,验证用户的访问权限 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String username = (String) principals.getPrimaryPrincipal(); User user = userService.findByUsername(username); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); Set<String> roles = user.getRoles(); authorizationInfo.setRoles(roles); Set<String> permissions = user.getPermissions(); authorizationInfo.setStringPermissions(permissions); return authorizationInfo; } } 4.使用JWT进行身份验证和授权管理 在Spring Boot应用程序中,使用ShiroJWT来进行身份验证和授权管理的流程大致如下: 4.1 前端用户进行登录操作,将用户名和密码发送到后台服务。 4.2 后台服务进行身份验证,将用户身份信息生成JWT并返回给前端。 4.3 前端保存JWT,并在后续的请求中将其发送到后台服务。 4.4 后台服务验证JWT的有效性,并根据用户的角色和权限信息进行访问控制。 综上所述,Spring Boot、ShiroJWT可以很好地配合使用,实现Web应用程序的身份验证和授权管理。这种组合方案可以提高开发效率和系统安全性,同时也适用于微服务架构中对身份验证和授权的需求。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值