前言

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. 报错AttributeError: Attempted to set WANDB to False, but CfgNode is immutable

    问题  今天在跑代码的时候,使用到了wandb记录训练数据.  我在23服务器上跑的好好的,但将环境迁移到80服务器上重新开始跑时,却遇到了如下报错  看这个报错信息是由于wandb没有apis这个属 ...

  2. linux常见命令(四)

    用于查看日期和时间的相关命令 cal date hwclock cal:显示日历信息 命令语音:cal [选项] [[[日]月]年] 选项 选项含义 -j 显示出给定月中的每一天是一年总的第几天(从1 ...

  3. [GXYCTF 2019]Ping Ping Ping

    题目就是ping,而且这还有一个查询窗口,就随便查询试试 ping了一下本地,发现没有什么很大的作用,给出了提示是php可以执行系统函数这就感到神奇了,就还是上网搜了搜 发现可以在查询IP后面进行拼接 ...

  4. PXE批量网络装机

    PXE高效批量网络装机 系统装机的三种引导方式 1.硬盘 2.光驱(u盘) 3.网络启动 pxe 系统安装过程 加载boot loader Boot Loader 是在操作系统内核运行之前运行的一段小 ...

  5. 关于PaddleOCR识别时中文路径导致报错/没输出结果

    此处只做学习PaddleOCR时遇到的一些坑 一.Python版本与PaddleOCR兼容性问题 如果你在Python11的环境下安装PaddlePaddle,使用 paddleocr --image ...

  6. QMatrix类

    QMatrix类指定了坐标系统的2D转换.QMatrix类可以进行平移,旋转,缩放,扭曲 m11 m12 0 m21 m22 0 dx dy 1 x' = m11x + m21y + dx y' = ...

  7. Java比赛常用API总结

    1.栈和队列 1.1 栈的常用方法 //1.栈顶插入元素 push(element) //2.返回栈顶元素并弹出栈顶元素 pop() //3.返回栈顶元素但不弹出 peek() //4.清空栈 cle ...

  8. 一起来探索CSS中margin属性的奥秘吧!!

    作者:WangMin 格言:努力做好自己喜欢的每一件事 众所周知 margin属性 是用来声明当前所设置或者指定元素所有外边距的宽度,或者设置各边上外边距的宽度.一直以来我认为它是一个很简单的属性,但 ...

  9. "拍牌神器"是怎样炼成的(三)---注册全局热键

    要想在上海拍牌的超低中标率中把握机会.占得先机,您不仅需要事先准备好最优的竞拍策略,还要制定若干套应急预案,应对不时之需.既定策略交给计算机自动执行,没有问题.可是谁来召唤应急预案呢?使用全局热键应该 ...

  10. NativeBuffering,进一步提升字符串的序列化性能

    在<NativeBuffering,一种高性能.零内存分配的序列化解决方案[性能测试篇]>我比较了NativeBuffering和System.Text.Json两种序列化方式的性能,通过 ...