前言

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. js递归查询之根据id查询当前对象

    需求:递归查询数组中id对应的数据 Json数据格式: 1 let cityArr = [ 2 { 3 id: 1000, 4 name: '四川省', 5 children: [ 6 { 7 id: ...

  2. Linux下安装MySQL问题及报错解决

    前言: 在Linux环境下,安装MySQL服务 环境: 虚拟机CentOS7 \-----------------------------------------------\ 流程: 确保mysql ...

  3. Jackson--FastJson--XStream--代码执行&&反序列化

    Jackson--FastJson--XStream--代码执行&&反序列化 Jackson代码执行 (CVE-2020-8840) 影响范围 2.0.0 <= FasterXM ...

  4. Macos下用Clion调试chromium源码

    1:下载CLion 2021.1.3(网上有破解版) 2:选择File->Open 导入chromium源码 3:在Src同级目录新建一个CMakeLists.txt 4:点击clion编译按钮 ...

  5. pta乙级1033(C语言)散列表解法

    #include"stdio.h" #include"string.h" int main() { int flag=1; char w[100010],ch[ ...

  6. JAVA类的加载(2) ——按需加载(延迟加载)

    1.例1: 1 /* 2 按需加载:当你不去实例化Cat时,Cat相关类都不会被加载,即按需加载(需要时加载) 3 1.先加载父类 4 2.初始化类 5 3.类只加载一次(暂且这么认为)--缓存 6 ...

  7. Spring3.0中的AOP配置方法

    http://zywang.iteye.com/blog/974226 http://www.cnblogs.com/garinzhang/p/java_spring_aop_aspect.html ...

  8. [Python急救站课程]输出水仙花数

    python #d = 0 print("所有三位数中的水仙花数如下所示:") for i in range(100, 1000): a = i // 100 # 求百位数 b = ...

  9. influxdb 连续查询使用总结

    转载请注明出处: 1.定义: InfluxDB 连续查询(Continuous Query)是一种自动化查询类型,该查询会根据定义的时间间隔定期运行,并将结果存储在新的目标测量中.这样的查询通常用于处 ...

  10. mysql常用函数详解

    1. Mysql内置函数分类及使用范围 数学函数: 这类函数只要用于处理数字.这类函数包括绝对值函数.正弦函数.余弦函数.获取随机数函数等. 字符串函数:这类函数主要用于处理字符串.其中包括字符串连接 ...