shiro基于角色URL进行鉴权
前言
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进行鉴权的更多相关文章
- Shiro(4)默认鉴权与自定义鉴权
=========默认鉴权======== 过滤链中定义: <!-- 过滤链定义 --> <property name="filterChainDefinitions&qu ...
- 基于token机制鉴权架构
常见的鉴权方式有两种,一种是基于session,另一种是基于token方式的鉴权,我们来浅谈一下两种 鉴权方式的区别. 两种鉴权方式对比 session 安全性:session是基于cookie进行用 ...
- 基于SpringAop的鉴权功能
什么是 AOP 首先我们先了解一下什么是AOP,AOP(Aspect Orient Programming),直译过来就是面向切面编程.AOP是一种编程思想,是面向对象编程(OOP)的一种补充.面向对 ...
- Python 计算AWS4签名,Header鉴权与URL鉴权
AWS4 版本签名计算参考 #!/usr/bin/env python3 # -*- coding:utf-8 -*- # @Time: 2021/7/24 8:12 # @Author:zhangm ...
- Mongodb 认证鉴权那点事
[TOC] 一.Mongodb 的权限管理 认识权限管理,说明主要概念及关系 与大多数数据库一样,Mongodb同样提供了一套权限管理机制. 为了体验Mongodb 的权限管理,我们找一台已经安装好的 ...
- 阿里云直播鉴权java代码示例
段时间公司需要做直播服务,所以就研究了一下阿里云的直播,在直播里面,最重要的就是url的鉴权操作(验证推流或者拉流的有效性),在网上找了很多代码,都没有发现java的demo,所以就写篇播客记录一下, ...
- shiro,基于springboot,基于前后端分离,从登录认证到鉴权,从入门到放弃
这个demo是基于springboot项目的. 名词介绍: ShiroShiro 主要分为 安全认证 和 接口授权 两个部分,其中的核心组件为 Subject. SecurityManager. Re ...
- shiro jwt 构建无状态分布式鉴权体系
一:JWT 1.令牌构造 JWT(json web token)是可在网络上传输的用于声明某种主张的令牌(token),以JSON 对象为载体的轻量级开放标准(RFC 7519). 一个JWT令牌的定 ...
- spring boot / cloud (十四) 微服务间远程服务调用的认证和鉴权的思考和设计,以及restFul风格的url匹配拦截方法
spring boot / cloud (十四) 微服务间远程服务调用的认证和鉴权的思考和设计,以及restFul风格的url匹配拦截方法 前言 本篇接着<spring boot / cloud ...
- 基于Springboot集成security、oauth2实现认证鉴权、资源管理
1.Oauth2简介 OAuth(开放授权)是一个开放标准,允许用户授权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容,OAu ...
随机推荐
- Modbus转Profinet网关改写变频器运行状态在1200PLC程序控制实例
兴达易控Modbus转Profinet网关将丹佛斯变频器接入西门子 1200PLC 配置案例 案例简介: 本案例是兴达易控Modbus转Profinet网关连接丹佛斯变频器在西门子 1200PLC程序 ...
- 一套基于 .NET Core 开发的支付SDK集 - paylink
前言 在我们的日常工作开发中对接一些第三方支付是比较常见的,如最常见的就是支付宝.微信支付的对接.今天给大家推荐一个基于.NET Core开发的支付SDK集:paylink,它极大简化了API调用及通 ...
- Python正则表达式——常用re正则表达式集合
文章目录 一.校验数字的表达式 二.校验字符的表达式 三.特殊需求表达式 一.校验数字的表达式 数字:^[0-9]*$ n位的数字:^\d{n}$ 至少n位的数字:^\d{n,}$ m-n位的数字:^ ...
- Asp-Net-Core开发笔记:快速在已有项目中引入EFCore
前言 很多项目一开始选型的时候没有选择EFCore,不过EFCore确实好用,也许由于种种原因后面还是需要用到,这时候引入EFCore也很方便. 本文以 StarBlog 为例,StarBlog 目前 ...
- ProcessingJS
ProcessingJS 图形 rect(x, y, w, h)(在新窗口中打开) ellipse(x, y, w, h) triangle(x1, y1, x2, y2, x3, y3) line( ...
- 🎁平平无奇的 Docker 命令(日常流)
Docker search docker search 命令用于在 Docker Hub 上搜索镜像,语法如下: docker search [OPTIONS] TERM 常用的选项包括: --fil ...
- 使用visualvm远程监控JVM
参考:http://blog.sina.com.cn/s/blog_4e90b3ba0100muco.html 1.首先要修改JDK中JMX服务的配置文件,以获得相应的权限: 在jdk-1.6.0_2 ...
- Python 用户输入和字符串格式化指南
Python 允许用户输入数据.这意味着我们可以向用户询问输入.在 Python 3.6 中,使用 input() 方法来获取用户输入.在 Python 2.7 中,使用 raw_input() 方法 ...
- IoC容器趣谈
今天我们来谈谈Spring的内核之一--IoC容器 大家可能会有这样的疑问: "这玩意为啥要叫容器呢?好奇怪" "容器不是装东西的吗?难道IoC容器也是用来装什么东西的? ...
- HDL刷题:Edgedetect
原题链接 一道想了好久的题目,在这种并行执行的程序里怎么才能保存前一个状态,看了题解后才发觉,非阻塞赋值啊,代码如下: module top_module ( input clk, input [7: ...