前言

shiro基于URL进行鉴权,网上有很多,但是多数都是copy不排版,眼睛都看花了,还不如自己看看源码。
2021年1月14日21:23:49最新的shiro是1.7,使用时发现了首次访问的一个bug,然后我使用1.5.3
环境springboot 2.3.7.RELEASE + thymeleaf + shiro 1.5.3

	<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.5.3</version>
</dependency>

自定义过滤

一、登录

我并没有用到继承AuthorizingRealm来实现登录,自己进行手动验证、加密

  //我们不使用shiro的密码认证,在此验证
//自己认证, 账号密码认证 采用 MD5 + 随机盐salt
//密码加密方式:密码第二位插入盐 再md5加密,例如密码是123,盐是Po*-,那么加盐后是1Po*-23 再md5
@ApiOperation("登录")
@PostMapping("login")
@ResponseBody
public ResponseResult login(String username, String password) {
Assert.notBlank(username, "账号不能为空!");
Assert.notBlank(username, "密码不能为空!");
try {
//查询数据库
SUser user = userService.getUserByUsername(username);
if (user == null) {
throw new UnknownAccountException("账号不存在");
}
if (user.getStatus() != 1) {
return new ResponseResult(HttpCode.FAIL, "当前账号无效或被停止使用!");
}
if (!CommonUtils.passwordToMD5(password, user.getSalt()).equals(user.getPassword())) {
throw new IncorrectCredentialsException("密码错误!");
} //通过安全工具类获取主体,初始化时自定注入了
Subject subject = SecurityUtils.getSubject();
//创建token
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
subject.login(token);
//信息存会话中
User info = new User();
info.setId(user.getId());
info.setUsername(user.getUsername());
info.setNickname(user.getNickname());
request.getSession().setAttribute("user", info);
} catch (UnknownAccountException e) {
log.error("账号不存在!");
return new ResponseResult(HttpCode.FAIL, e.getMessage());
} catch (IncorrectCredentialsException e) {
log.error("无效的凭据,密码错误!");
return new ResponseResult(HttpCode.FAIL, "无效的凭据,密码错误!");
} catch (LockedAccountException e) {
log.error("当前账号被锁定,无法登录");
return new ResponseResult(HttpCode.FAIL, "当前账号被锁定,无法登录");
} catch (AuthenticationException e) {
log.error("授权过程中的异常:{}", e.getMessage());
return new ResponseResult(HttpCode.FAIL, "登录异常" + e.getMessage());
} //登录后,返回原访问页面
SavedRequest savedRequest = WebUtils.getSavedRequest(request);
if (savedRequest != null) {
// 登录前url
return new ResponseResult(HttpCode.OK, "登录成功!", savedRequest.getRequestUrl());
}
String referer = request.getParameter("referer");
if (StrUtil.isBlank(referer)) {
referer = "/";
} else if (referer.contains("/register")) {
referer = "/";
}
return new ResponseResult(HttpCode.OK, "登录成功!", referer);
}

二、自定义过滤

查询当前URL需要哪些角色,没有就走到401返回JSON

import com.example.shirodemo.service.PermissionService;
import java.util.List;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired; /**
* @author 绫小路
* @date 2021/1/10 19:54
* @description
*/
public class PermissionFilter extends AccessControlFilter { private static final String UN_RESPONSE_MESSAGE = "{\"timestamp\":%d,\"code\":1,\"message\":\"未授权资源\"}"; @Autowired
private PermissionService permissionService; @Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
Subject subject = SecurityUtils.getSubject();
//判断是否登录
if (!subject.isAuthenticated()) {
// 通过阅读源码,AccessControlFilter的protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response)
// 才是跳转到登录的正确姿势,网上大多数姿势采用重定向,采用的重定向无法保存访问记录,登录后无法回到原访问页面
saveRequestAndRedirectToLogin(request, response);
}
HttpServletRequest httpRequest = WebUtils.toHttp(request);
String uri = httpRequest.getRequestURI();
//获取访问此URL的角色
List<String> roles = permissionService.getPermissionRole(uri);
if (!roles.isEmpty()) {
if (subject.hasRoles(roles).length > 0) {//有此角色,放行
return true;
}
} //401 返回json数据,此处可灵活使用 HttpServletRequest HttpServletResponse 转发到你想要的页面
HttpServletResponse httpResponse = WebUtils.toHttp(response);
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpResponse.setHeader("Content-type", "application/json;charset=utf-8");
httpResponse.setCharacterEncoding("UTF-8");//防止中文乱码
httpResponse.getWriter().write(String.format(UN_RESPONSE_MESSAGE, System.currentTimeMillis()));
return false;
} @Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
return false;
}
}

三、配置

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.example.shirodemo.config.custom.PermissionFilter;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
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; /**
* @author 绫小路
* @date 2021/1/10 16:55
* @description
*/
@Configuration
public class ShiroConfig { //1 SecurityManager 流程控制
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("customAuthorizingRealm")CustomAuthorizingRealm customAuthorizingRealm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(customAuthorizingRealm);
//do something...
return defaultWebSecurityManager;
} //2 ShiroFilterFactoryBean 请求过滤器
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//添加Shiro内置过滤器
/**
* Shiro内置过滤器,可以实现权限相关的拦截器
* 常用的过滤器:
* anon: 无需认证(登录)可以访问
* authc: 必须认证才可以访问
* user: 如果使用rememberMe的功能可以直接访问
* perms: 该资源必须得到资源权限才可以访问
* role: 该资源必须得到角色权限才可以访问
*/
Map<String, String> filterMap = new HashMap<String, String>();
//授权过滤器
//注意:当前授权拦截后,shiro会自动跳转到未授权页面
//perms括号中的内容是权限的值
// filterMap.put("/add", "perms[user:add]");
filterMap.put("/admin/**", "authc"); filterMap.put("/swagger-ui/**", "authc");
filterMap.put("/v2/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap); //登录页面
shiroFilterFactoryBean.setLoginUrl("/login"); //自定义过滤
Map<String, Filter> filter = new HashMap<>();
//添加自定义过滤器
filter.put("authc", permissionFilter());
shiroFilterFactoryBean.setFilters(filter);
return shiroFilterFactoryBean;
} /**
* 注入自定义过滤器
*/
@Bean
public PermissionFilter permissionFilter() {
return new PermissionFilter();
}
}

CustomAuthorizingRealm.java

import com.example.shirodemo.service.UserService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; /**
* @author 绫小路
* @date 2021/1/10 16:58
* @description
*/
@Component //交给spring托管
public class CustomAuthorizingRealm extends AuthorizingRealm { @Autowired
private UserService userService; //授权,每次访问都会授权一次,不缓存会对数据库造成压力
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println(11111);
return null;
} //认证, 账号密码认证 采用 MD5 + 随机盐salt
//密码加密方式:密码第二位插入盐 再md5加密,例如密码是123,盐是Po*-,那么加盐后是1Po*-23 再md5
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//因为在controller中做了认证,这里直接返回即可
return new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), this.getName());
}
}

效果图

正常访问:

登录后未授权访问

shiro基于角色URL进行鉴权的更多相关文章

  1. Shiro(4)默认鉴权与自定义鉴权

    =========默认鉴权======== 过滤链中定义: <!-- 过滤链定义 --> <property name="filterChainDefinitions&qu ...

  2. 基于token机制鉴权架构

    常见的鉴权方式有两种,一种是基于session,另一种是基于token方式的鉴权,我们来浅谈一下两种 鉴权方式的区别. 两种鉴权方式对比 session 安全性:session是基于cookie进行用 ...

  3. 基于SpringAop的鉴权功能

    什么是 AOP 首先我们先了解一下什么是AOP,AOP(Aspect Orient Programming),直译过来就是面向切面编程.AOP是一种编程思想,是面向对象编程(OOP)的一种补充.面向对 ...

  4. Python 计算AWS4签名,Header鉴权与URL鉴权

    AWS4 版本签名计算参考 #!/usr/bin/env python3 # -*- coding:utf-8 -*- # @Time: 2021/7/24 8:12 # @Author:zhangm ...

  5. Mongodb 认证鉴权那点事

    [TOC] 一.Mongodb 的权限管理 认识权限管理,说明主要概念及关系 与大多数数据库一样,Mongodb同样提供了一套权限管理机制. 为了体验Mongodb 的权限管理,我们找一台已经安装好的 ...

  6. 阿里云直播鉴权java代码示例

    段时间公司需要做直播服务,所以就研究了一下阿里云的直播,在直播里面,最重要的就是url的鉴权操作(验证推流或者拉流的有效性),在网上找了很多代码,都没有发现java的demo,所以就写篇播客记录一下, ...

  7. shiro,基于springboot,基于前后端分离,从登录认证到鉴权,从入门到放弃

    这个demo是基于springboot项目的. 名词介绍: ShiroShiro 主要分为 安全认证 和 接口授权 两个部分,其中的核心组件为 Subject. SecurityManager. Re ...

  8. shiro jwt 构建无状态分布式鉴权体系

    一:JWT 1.令牌构造 JWT(json web token)是可在网络上传输的用于声明某种主张的令牌(token),以JSON 对象为载体的轻量级开放标准(RFC 7519). 一个JWT令牌的定 ...

  9. spring boot / cloud (十四) 微服务间远程服务调用的认证和鉴权的思考和设计,以及restFul风格的url匹配拦截方法

    spring boot / cloud (十四) 微服务间远程服务调用的认证和鉴权的思考和设计,以及restFul风格的url匹配拦截方法 前言 本篇接着<spring boot / cloud ...

  10. 基于Springboot集成security、oauth2实现认证鉴权、资源管理

    1.Oauth2简介 OAuth(开放授权)是一个开放标准,允许用户授权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容,OAu ...

随机推荐

  1. EtherCAT转Modbus网关做为 MODBUS 从站配置

    EtherCAT转Modbus网关做为 MODBUS 从站配置案例 兴达易控EtherCAT转Modbus网关可以用作MODBUS从站的配置.这种网关允许将Modbus协议与EtherCAT协议进行转 ...

  2. 新零售SaaS架构:面向中小连锁的SaaS系统整体规划

    零售企业的发展路径 零售企业的发展路径一般可分为以下几个阶段: 单店经营阶段:企业在一个地区或城市开设单个门店.这时,企业需要把精力放在了解当地市场和顾客需求上,这是积累经验和品牌知名度的重要环节.为 ...

  3. centos7.9 扩容swap分区

    情况说明:在VMware vsphere的虚拟化平台下,为了快速部署虚拟服务器,我们常常使用模板部署虚拟机.但真实业务有时要求的文件系统分区和大小常常与模板不同,这时便需要自定义硬件资源和使用 LVM ...

  4. DotNetGuide新增C#/.NET/.NET Core充电站(让你学习不迷路)

    DotNetGuide简介 记录.收集和总结C#/.NET/.NET Core基础知识.学习路线.开发实战.学习视频.文章.书籍.项目框架.社区组织.开发必备工具.常见面试题.面试须知.简历模板.以及 ...

  5. div 让a内容居中方法

    <div>标签是HTML中的一个重要标签,它代表了一个文档中的一个分割区块或一个部分.在<div>标签中,我们可以放置各种内容,包括文本.图像.链接等等.有时候,我们需要将其中 ...

  6. Django框架项目之登录注册——1-登录注册页面、2 多方式登录、3-手机是否存在验证接口、4-腾讯云短信开发、5 短信验证码接口、6-短信登录接口、7-短信注册接口、8-前台登录注册修订

    文章目录 1-登录注册页面 模态登录组件 模态注册组件 导航条:结合实际情况完成样式 登录业务分析 多方式登录 验证码登录 注册业务分析 验证码注册 汇总 2 多方式登录 后台 插件 urls.py ...

  7. os --- 多种操作系统接口¶

    os.path --- 常用路径操作 源代码: Lib/posixpath.py (用于 POSIX) 和 Lib/ntpath.py (用于 Windows). 此模块实现了一些有用的路径名称相关函 ...

  8. 如何使用Arduino创建摩尔斯电码生成器

    摩尔斯电码工作原理 摩尔斯电码发明于19世纪,使用非常简单的长短脉冲序列(通常为电和划)来远距离发送消息.通过将字母表中的字母编码为电和划的组合,信息可以只用一个单一的电子或声音信号来表达. 为了说明 ...

  9. 打造炫酷效果:用Java优雅地制作Excel迷你图

    摘要:本文由葡萄城技术团队原创并首发.转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 前言 迷你图是一种简洁而有效的数据可视化方式,常用于展示趋势和变化.它 ...

  10. SOA认知和方法论

    1 前言 1.1 架构分类 在软件设计领域,企业架构通常被划分为如下五种分类: 如何理解架构分类依据及其彼此之间的关系?业务是企业赖以生存之本,因此业务架构是基础.是灵魂,其他一切均是对业务架构的支撑 ...