shiro-spring-boot-starter

news2024/9/22 1:02:47

第2.1.7章 WEB系统最佳实践Spring文件配置之spring-shiro.xml
2016年还在使用shiro,后来使用应用springboot之后,因为有了网关,感觉网关就可以做一些拦截,就没必要一定要使用shiro,如果你使用平台还需要每个系统自己做权限拦截吗,除非有人绕开网关,直接公司后台的系统,那么这些后台系统的安全就崩溃了。可是如果你绕开不了网关,就不是那么容易了。
最近做后台系统或者本地化系统,就要求简单使用,而不是非得用平台式做法,搞那么多微服务,于是又将shiro捡起来了。shiro-spring-boot-starter不要轻易使用1.10.1当前最新版本,因为出现的一些错误,在互联网上找不到答案,除非你有时间仔细看官网的资料。但快节奏要掌握很多“无用知识”的我们,还是先交活,把架子搞起来,再做优化。
本地化就不需要一定要使用,多一个redis我都嫌,不得不捡起第2.1.7章 WEB系统最佳实践Spring文件配置之spring-shiro.xml
如果能支持caffeine就好了,或许可以自己扩展,但当前没有时间研究。先还是讲究如何落地吧。
参考的文章有
SpringBoot+Shiro+Vue实现身份验证
Shiro和SpringBoot集成前后端分离登陆验证和权限验证接口302获取不到返回结果的问题
Spring Boot + shiro 去除Redis缓存

 <shiro.version>1.6.0</shiro.version>
<shiro-ehcache.version>1.4.2</shiro-ehcache.version>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-starter</artifactId>
    <version>${shiro.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>${shiro-ehcache.version}</version>
</dependency>
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;


public class UserRealm extends AuthorizingRealm {

    @Lazy
    @Autowired
    private SysUserService sysUserService;


    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
        SysUser sysUser = sysUserService.selectByPhone(token.getUsername());
        if (sysUser != null){
            return new SimpleAuthenticationInfo(sysUser, sysUser.getPassword(), ByteSource.Util.bytes(sysUser.getSalt()),getName());
        }
        return null;
    }
}
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;

public class CustomDefaultWebSessionManager extends DefaultWebSessionManager {
    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        String sessionId = WebUtils.toHttp(request).getHeader("Authorization");
        // 如果请求头中有 Authorization 则其值为sessionId
        if (CheckEmptyUtil.isEmpty(sessionId)) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, ShiroHttpServletRequest.URL_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionId);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return sessionId;
        } else {
            //否则按默认规则从cookie取sessionId
            return super.getSessionId(request, response);
        }
    }

}
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

/**
 * 自定义权限
 */
public class CustomPermissionsAuthorizationFilter extends PermissionsAuthorizationFilter {
    /**
     * 根据请求接口路径进行验证
     * @param request
     * @param response
     * @param mappedValue
     * @return
     * @throws IOException
     */
    @Override
    public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
        // 获取接口请求路径
        String servletPath = WebUtils.toHttp(request).getServletPath();
        mappedValue = new String[]{servletPath};
        return super.isAccessAllowed(request, response, mappedValue);
    }

    /**
     * 解决权限不足302问题
     * @param request
     * @param response
     * @return
     * @throws IOException
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        Subject subject = getSubject(request, response);
        if (subject.getPrincipal() == null) {
            saveRequestAndRedirectToLogin(request, response);
        } else {
            ResponseResult resp = new ResponseResult(false, "无访问权限");
            WebUtils.toHttp(response).setContentType("application/json; charset=utf-8");
            WebUtils.toHttp(response).getWriter().print(resp);
        }
        return false;
    }
}
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 *
 * 重写权限验证问题,登录失效302后返回状态码
 *
 */
@Slf4j
public class ShiroFormAuthenticationFilter extends FormAuthenticationFilter {

    /**
     * 屏蔽OPTIONS请求
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        boolean accessAllowed = super.isAccessAllowed(request, response, mappedValue);
        if (!accessAllowed) {
            // 判断请求是否是options请求
            String method = WebUtils.toHttp(request).getMethod();
            if (StringUtils.equalsIgnoreCase("OPTIONS", method)) {
                return true;
            }
        }
        return accessAllowed;
    }


    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        if (isLoginRequest(request, response)) {
            if (isLoginSubmission(request, response)) {
                return executeLogin(request, response);
            } else {
                return true;
            }
        } else {
            // 返回固定的JSON串
            ResponseResult resp = new ResponseResult(false, "未登录");
            WebUtils.toHttp(response).setContentType("application/json; charset=utf-8");
            WebUtils.toHttp(response).getWriter().print(resp);
            return false;
        }
    }

}
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class JwtConfiguration {

    @Value("${hashIterations:0}")
    public int hashIterations;

    @Value("${hashAlgorithmName:}")
    public String hashAlgorithmName;

    @Value("${saltSize:0}")
    public int saltSize;

    @Value("${jwt.expire:0}")
    public int expire;

    @Value("${jwt.token-header:}")
    public String tokenHeader;

    @Value("${jwt.rsa-secret:}")
    public String rsaSecret;

}

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

@Configuration
public class ShiroConfig {


    @Autowired
    private JwtConfiguration jwtConfiguration;

    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 设置自定义的过滤器
        Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
        filters.put("authc", new ShiroFormAuthenticationFilter());
        filters.put("perms", new CustomPermissionsAuthorizationFilter());
        // 配置过滤器
        Map<String, String> filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap();
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/login", "anon");
//        filterChainDefinitionMap.put("/**", "authc,perms"); // 登录+授权
        filterChainDefinitionMap.put("/**", "anon");
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        return shiroFilterFactoryBean;
    }

    @Bean
    public DefaultWebSecurityManager securityManager(UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        securityManager.setSessionManager(this.sessionManager());
        //设置缓存
        securityManager.setCacheManager(getCacheManager());
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }


    @Bean
    public UserRealm userRealm() {
        UserRealm myShiroRealm = new UserRealm();
        myShiroRealm.setCredentialsMatcher(this.hashedCredentialsMatcher());
        return myShiroRealm;
    }

    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName(jwtConfiguration.hashAlgorithmName);
        hashedCredentialsMatcher.setHashIterations(jwtConfiguration.hashIterations);
        return hashedCredentialsMatcher;
    }

    @Bean
    public SimpleCookie getSimpleCookie() {
        SimpleCookie cookie = new SimpleCookie();
        cookie.setName("kfyy.session.id");
        cookie.setPath("");
        cookie.setHttpOnly(false);
        return cookie;
    }

    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        sessionManager.setSessionIdCookie(this.getSimpleCookie());
//        sessionManager.setSessionDAO(this.redisSessionDAO());
//        sessionManager.setGlobalSessionTimeout(this.sessiontimeout);
        return sessionManager;
    }

    /**
     *
     * 缓存框架
     * @return
     */
    @Bean
    public EhCacheManager getCacheManager(){
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManagerConfigFile("classpath:shiro-ehcache.xml");
        return ehCacheManager;
    }

    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

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


}

<?xml version="1.0" encoding="UTF-8" ?>
<ehcache>
    <defaultCache
            maxElementsInMemory="1000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>

1
看看controler的写法和ajax的写法,就知道问题在哪里了。
1
因为Content-type使用了application/json,故而需要使用@RequestBody来取值,因为参数在payload中,不像application/x-www-form-urlencoded,是在url参数中。
1
这样针对application/json就需要按照下面的方法才能取到值。

    @ApiOperation("登录")
    @PostMapping("login")
    public ResponseResult<LoginResultDto> login(@RequestBody UserVo userVo){
        String username = userVo.getUsername();
        String password = userVo.getPassword();
        Subject subject = SecurityUtils.getSubject()

接着再看vue侧的写法

import axios, { AxiosRequestConfig, AxiosInstance } from 'axios'
public post = (url: string, data = {}, config: AxiosRequestConfig<any> = {}): Promise<any> =>
		this.instance({ url, method: 'post', data, ...config })

如果默认采用application/json问题是,有两个问题
1 controller接收参数需要一堆碎片化的dto,如果参数不多,那么我们的请求为什么一定要用application/json
前端换种请求,就可以识别application/x-www-form-urlencoded

public postForm = (url: string, data = {} , config: AxiosRequestConfig<any> = {}): Promise<any> =>
	axios({
		...this.baseConfig,
		headers:{
			...this.baseConfig.headers,
			'Content-Type': "application/x-www-form-urlencoded"
		},
		url,
		method: 'post',
		data,
		...config,
	})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/79900.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小白学编程(Java)20:判断一个数是否是质数

解题思路&#xff1a; 第一步&#xff1a;弄明白质数是什么&#xff1f; 质数的定义&#xff1a;质数又称素数。一个大于1的自然数&#xff0c;除了1和它自身外&#xff0c;不能被其他自然数整除的数叫做质数&#xff1b;否则称为合数&#xff08;规定1既不是质数也不是合数&am…

微信公众号开发——获取AccessToken接口调用凭据

&#x1f60a; 作者&#xff1a; 一恍过去&#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390&#x1f38a; 社区&#xff1a; Java技术栈交流&#x1f389; 主题&#xff1a; 微信公众号开发——获取AccessToken接口调用凭据⏱️ 创作时间&#xff1…

Typora的使用

一&#xff0e;标题&#xff1a;六级标题 1 .一级标题 2. 二级标题 二级标题 &#xff1a; ## 加一个空格 现在加 ## 空格 然后我们把光标放在其他地方 点击一下 3 层级列表 层级列表: > 空格 回车 在这一部分&#xff0c;我们先来讲解三个概念&#xff1a…

基于java+springboot+mybatis+vue+mysql的篮球竞赛预约平台

项目介绍 随着人们生活质量提高&#xff0c;越来越多的年轻人喜欢足球。尤其是在高校之中&#xff0c;每年都会举办一些足球和篮球比赛。甚至在国际上每年也都会有一足球和篮球联赛。很多高校为了提高学生的身体素质每年也都会举办一些篮球竞赛&#xff0c;为了让这些篮球爱好…

计算机毕业设计ssm+vue基本微信小程序的奶茶点单系统

随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受客户的喜爱&#xff0c;随便点奶茶点单小程序被客户普遍使用&#xff0c;为方便客户能够…

《发现心流》

关于作者 米哈里•契克森米哈赖&#xff0c;任教于美国加州克 菜蒙特大学&#xff0c;提出并发展了心流理论。他 一直在关注人类的积极心理体验&#xff0c;完成了 大量经典的研究工作&#xff0c;是该领域最权威的 学者之一&#xff0c;被誉为积极心理学的世界级领 军人物。 …

日期与时间

目录 一、基本概念 1.1、日期和时间 1.2、本地时间 1.3、时区 1.4、夏令时 1.5、本地化 二、Date 和 Calendar 2.1、Date 2.2、Calendar 三、TimeZone 四、LocalDateTime 4.1、LocalDateTime 4.2、Duration和Period 五、ZoneDateTime 5.1、创建实例 5.2、时区转…

电子学会2020年12月青少年软件编程(图形化)等级考试试卷(一级)答案解析

目录 一、单选题&#xff08;共25题&#xff0c;每题2分&#xff0c;共50分&#xff09; 二、判断题&#xff08;共10题&#xff0c;每题2分&#xff0c;共20分&#xff09; 三、编程题【该题由测评师线下评分】&#xff08;共2题&#xff0c;共30分&#xff09; 青少年软件…

【MySQL数据库】索引到底是什么,怎么创建索引的效率最高

面试官&#xff1a;我看你简历上写了MySQL&#xff0c;对MySQL InnoDB引擎的索引了解吗&#xff1f; 候选者&#xff1a;嗯啊&#xff0c;使用索引可以加快查询速度&#xff0c;其实上就是将无序的数据变成有序&#xff08;有序就能加快检索速度&#xff09; 候选者&#xff…

微服务守护神-Sentinel-其他

引言 书接上篇 微服务守护神-Sentinel-热点-授权-系统规则 &#xff0c;上面介绍了Sentinel热点、授权、系统规则&#xff0c;本篇继续来Sentinel 剩下其他操作 自定义异常返回 当触发sentinel流控规则之后&#xff0c; sentinel就干巴巴返回异常信息&#xff0c;单纯的文字…

JVM监控及诊断工具

本文目录命令行jps 查看正在运行的Java进程jstat&#xff1a;查看JVM统计信息jinfo&#xff1a;实时查看和修改JVM配置参数jmap&#xff1a;导出内存映像文件&内存使用情况jhat&#xff1a;JDK自带堆分析工具jstack&#xff1a;打印JVM中线程快照jcmd&#xff1a;多功能命令…

docker、LXC、LXD的区别及传统的虚拟机与操作系统虚拟化的区别

1. 概念解释 1.1. Docker Docker是一个用于在集中式平台上创建、部署和运行应用程序的开源工具。这使得主机的操作系统通过容器运行具有相同Linux内核的应用程序&#xff0c;而不是创建一个完整的虚拟机。使用docker容器不需要考虑Ram和磁盘空间的分配。它能够自己处理这些需…

CopyOnWriteArrayList源码解析

CopyOnWriteArrayList源码解析 简介 我们知道 ArrayList 是线程不安全的&#xff0c;其存在一个古老的线程安全的 Vector&#xff0c;但是由于 Vector 效率太低 (方法都加了synchronzed)&#xff0c;在 JDK1.5 时 Doug Lea 提供了一个效率较高的线程安全的 CopyOnWriteArrayL…

虚拟内存(深入理解计算机系统原书第3版9节)

深入理解计算机系统&#xff08;原书第3版&#xff09;读书笔记&#xff0c;其实就是嚼碎了原文然后把一部分挑了出来摘要&#xff0c;免得读着读着忘了 文章目录前言一、物理和虚拟寻址二、地址空间三、虚拟内存作为缓存的工具1、DRAM缓存的组织结构2、页表3、页命中4、缺页5、…

[附源码]Python计算机毕业设计SSM基于的小区物业管理系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

微服务框架 SpringCloud微服务架构 微服务保护 33 授权规则 33.1 授权规则

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 微服务保护 文章目录微服务框架微服务保护33 授权规则33.1 授权规则33.1.1 授权规则33 授权规则 33.1 授权规则 33.1.1 授权规则 看看sen…

康鹏科技将于12月16日上会:曾在纽交所上市,由杨建华家族控股

近日&#xff0c;上海康鹏科技股份有限公司&#xff08;下称“康鹏科技”&#xff09;在上海证券交易所递交招股书&#xff08;上会稿&#xff09;&#xff0c;准备在科创板上市。据贝多财经了解&#xff0c;康鹏科技将于2022年12月16日接受科创板上市委的现场审议。 根据公开信…

基于C++实现(MFC)职工工作量统计系统【100010022】

【职工工作量统计系统设计】 1、问题描述 职工包括姓名、职工号、性别、年龄、所在部门、联系方式等信息。 工作量包括职工号、完成的产品数量等信息。 该设计系统能够对职工的工作量进行统计&#xff0c;并排出名次。注意&#xff0c;一个职工的工作量是可以多次输入的。 2…

Alibaba官方上线,Java并发编程全彩图册(终极版)GitHub已置顶

都说程序员工资高、待遇好&#xff0c; 2022 金九银十到了&#xff0c;你的小目标是 30K、40K&#xff0c;还是 16薪的 20K&#xff1f;作为一名 Java 开发工程师&#xff0c;当能力可以满足公司业务需求时&#xff0c;拿到超预期的 Offer 并不算难。然而&#xff0c;提升 Java…

Python图像识别实战(一):实现按数量随机抽取图像复制到另一文件夹(附源码和实现效果)

前面我介绍了可视化的一些方法以及机器学习在预测方面的应用&#xff0c;分为分类问题&#xff08;预测值是离散型&#xff09;和回归问题&#xff08;预测值是连续型&#xff09;&#xff08;具体见之前的文章&#xff09;。 从本期开始&#xff0c;我将做一个关于图像识别的…