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 ...
随机推荐
- 43道Python经典案例题(有答案)
1.有四个数字:1.2.3.4,能组成多少个互不相同且无重复数字的三位数?各是多少? for x in range(0,5): for y in range(0,5): for z in range( ...
- 第七单元《中国传统文化与管理》单元测试 mooc
第七单元<中国传统文化与管理>单元测试 返回 本次得分为:8.00/10.00, 本次测试的提交时间为:2020-08-30, 如果你认为本次测试成绩不理想,你可以选择 再做一次 . 1 ...
- Kraft模式下Kafka脚本的使用
Kafka集群 版本:V3.5.1 名称 Node1 Node2 Node3 IP 172.29.145.157 172.29.145.182 172.29.145.183 (1)查看Kraft集群中 ...
- [WPF]原生TabControl控件实现拖拽排序功能
在UI交互中,拖拽操作是一种非常简单友好的交互.尤其是在ListBox,TabControl,ListView这类列表控件中更为常见.通常要实现拖拽排序功能的做法是自定义控件.本文将分享一种在原生控件 ...
- Tetris(俄罗斯方块).sh
#!/bin/bash # Tetris Game # 10.21.2003 xhchen<[email]xhchen@winbond.com.tw[/email]> #APP decla ...
- 银河麒麟V10 修改文件夹权限
并不建议修改系统文件夹的权限,防止终端失效 指令:获取所有权限 指令:写入可执行权限 chmod +x filename//filename 是文件路径 TRANSLATE with x Englis ...
- [转载]R2: 已解释和未解释的方差
估计值的方差与总体方差之间的差异就是回归方程对方差的解释率.试举一例,如图 1,身高与体重的回归线显示身高与体重之间呈正相关,Mr. Y身高76英寸体重220磅(图 1中插图.cdr的红点),他与体重 ...
- 2023-11-08:用go语言,字符串哈希原理和实现 比如p = 233, 也就是课上说的选择的质数进制 “ 3 1 2 5 6 ...“ 0 1 2 3 4 hash[0] = 3 * p的0
2023-11-08:用go语言,字符串哈希原理和实现 比如p = 233, 也就是课上说的选择的质数进制 " 3 1 2 5 6 ..." 0 1 2 3 4 hash[0] = ...
- 动态规划——提高Ⅴ(DP优化)
单调队列优化DP 其实单调队列就是一种队列内的元素有单调性(单调递增或者单调递减)的队列,答案(也就是最优解)就存在队首,而队尾则是最后进队的元素.因为其单调性所以经常会被用来维护区间最值或者降低DP ...
- 牛客小白月赛43 F 全体集合
题目链接 F 全体集合 题目大意 给出\(n\)个点\(m\)条边的无向图,给出\(k\)个点上分别有一个人,每个人一次只能走到一个相邻的节点,问有没有一种可能让这些人都走到一个点. 思路 考虑使用二 ...