15.权限控制 + 置顶、加精、删除

目录

1.权限控制

1.1 登录检查

1.2 授权配置

1.3 认证方案

1.4 CSRF 配置

2.置顶、加精、删除

2.1 开发数据访问层

2.2 业务层

2.3 表现层


Spring Security 是一个专注于为 Java 应用程序提供身份认证和授权的框架,它的强大之处在于它可以轻松扩展以满足自定义需求。

特征:对身份的认证和授权提供全面的、课可扩展的支持;防止各种攻击,如会话固定攻击、点击劫持、csrf攻击等;支持与 Servlet API、Spring MVC 等 Web 技术集成


1.权限控制

  • 登录检查:之前采用拦截器实现了登录检查,这是简单的权限管理方案,现在将其废弃
  • 授权配置:对当前系统内包含的所有的请求,分配访问权限(普通用户、版主、管理员)
  • 认证方案:绕过 Security 认证流程,采用系统原来的认证方案
  • CSRF 配置:防止 CSRF 攻击的基本原理,以及表单、AJAX 相关的配置

1.1 登录检查

引入依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>

废弃拦截器(WebMvcConfig 类中注掉 登录状态拦截器)

1.2 授权配置

首先在常量接口增加常量,在配置的时候需要指定哪些权限访问哪些用户

在 CommunityConstant 类中添加常量:

  • 普通用户权限、管理员权限、版主
    /*** 权限: 普通用户*/String AUTHORITY_USER = "user";/*** 权限: 管理员*/String AUTHORITY_ADMIN = "admin";/*** 权限: 版主*/String AUTHORITY_MODERATOR = "moderator";

在 config 包下新建 SecurityConfig 配置类:

  • 添加注解 @Configuration,并且继承 WebSecurityConfigurerAdapter,实现常量接口
  • 重写 configure(WebSecurity web) 方法:忽略对静态资源拦截(直接访问)
  • 重写 configure(HttpSecurity http) 方法:进行授权 和 权限不够的处理(当前项目中有多种请求(普通请求、异步请求),普通请求期望服务器返回 HTML,异步请求期望返回 JSON)
  • 权限不够的处理分为 没有登陆的处理和权限不足的处理(匿名实现接口)
  • 没有登陆的处理:判断同步异步请求——请求消息头某个值如果XMLHttpRequest 是异步请求,否则是同步请求
  • 如果是异步请求,给浏览器输出响应 JSON 字符串,手动处理(声明返回的类型),获取字符流,向前台输出内容,没有权限返回403
  • 同步请求:重定向到登陆页面 
  • 权限不足的处理:同上(重定向到没有权限的界面)
  • 处理没有权限路径(HomeController 类):
    //拒绝访问时的提示页面@RequestMapping(path = "/denied", method = RequestMethod.GET)public String getDeniedPage() {return "/error/404";}
  • Security底层默认会拦截 /logout 请求,进行退出处理;覆盖它默认的逻辑,才能执行我们自己的退出代码.
package com.example.demo.config;import com.example.demo.util.CommunityConstant;
import com.example.demo.util.CommunityUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter implements CommunityConstant {//重写 configure(WebSecurity web) 方法:忽略对静态资源拦截(直接访问)@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/resources/**");}//重写 configure(HttpSecurity http) 方法:进行授权 和 权限不够的处理@Overrideprotected void configure(HttpSecurity http) throws Exception {//授权http.authorizeRequests().antMatchers("/user/setting","/user/upload","/discuss/add","/comment/add/**","/letter/**","/notice/**","/like","/follow","/unfollow").hasAnyAuthority(AUTHORITY_USER,AUTHORITY_ADMIN,AUTHORITY_MODERATOR).anyRequest().permitAll().and().csrf().disable();// 权限不够时的处理http.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {//没有登陆@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response,AuthenticationException authException) throws IOException, ServletException {//判断同步异步请求——请求消息头某个值如果XMLHttpRequest 是异步请求,否则是同步请求String xRequestedWith = request.getHeader("x-requested-with");if ("XMLHttpRequest".equals(xRequestedWith)) {//如果是异步请求,给浏览器输出响应 JSON 字符串,手动处理(声明返回的类型),获取字符流//向前台输出内容,没有权限返回403response.setContentType("application/plain;charset=utf-8");PrintWriter writer = response.getWriter();writer.write(CommunityUtil.getJSONString(403, "你还没有登录哦!"));} else {//重定向到登陆页面response.sendRedirect(request.getContextPath() + "/login");}}}).accessDeniedHandler(new AccessDeniedHandler() {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response,AccessDeniedException accessDeniedException) throws IOException, ServletException {String xRequestedWith = request.getHeader("x-requested-with");if ("XMLHttpRequest".equals(xRequestedWith)) {response.setContentType("application/plain;charset=utf-8");PrintWriter writer = response.getWriter();writer.write(CommunityUtil.getJSONString(403, "你没有访问此功能的权限!"));} else {//重定向到没有权限的界面response.sendRedirect(request.getContextPath() + "/denied");}}});// Security底层默认会拦截/logout请求,进行退出处理.// 覆盖它默认的逻辑,才能执行我们自己的退出代码.http.logout().logoutUrl("/securitylogout");}
}

1.3 认证方案

Security框架中,会把认证信息封装到token里,token会被一个filter获取到,并存入security context里。之后授权的时候,都是从security context中获取token,根据token判断权限

1️⃣查询某用户的权限(UserService)

  • 根据 UserId 查询 用户,通过 type 判断权限(结果存入集合中)
  • 实例化集合,添加集合中的数据,实现方法:判断——1是管理员,2是版主, 3是普通用户
    public Collection<? extends GrantedAuthority>  getAuthorities(int userId) {User user = this.findUserById(userId);List<GrantedAuthority> list = new ArrayList<>();list.add(new GrantedAuthority() {@Overridepublic String getAuthority() {switch (user.getType()) {case 1:return AUTHORITY_ADMIN;case 2:return AUTHORITY_MODERATOR;default:return AUTHORITY_USER;}}});return list;}

2️⃣LoginTicket 拦截器在请求一开始就会判断凭证,可以在此时对用户进行认证,并构建用户认证的结果,存入 SecurityContext ,以便于 Security 进行授权(LoginTicketInterceptor)

  • 创建认证结果,存储到接口 Authentication 中(实现类 UsernamePasswordAuthenticationToken,通常存入三个数据:用户、密码、权限)
  • 需要存储到 SecurityContext 中,而 SecurityContext 是通过 SecurityContextHolder 去处理
    //实现 preHandle(执行具体方法之前的预处理)方法:在请求开始获得 ticket,利用 ticket 查找对应的 user@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//从 cookie 中获取凭证String ticket = CookieUtil.getValue(request,"ticket");if (ticket != null) {//查询凭证LoginTicket loginTicket = userService.findLoginTicket(ticket);//检查凭证是否有效:凭证不为空,并且状态是0,并且超时时间晚于当前时间才有效if (ticket != null && loginTicket.getStatus() == 0 && loginTicket.getExpired().after(new Date())) {// 根据凭证查询用户User user = userService.findUserById(loginTicket.getUserId());// 在本次请求中持有用户hostHolder.setUser(user);// 构建用户认证的结果,并存入SecurityContext,以便于Security进行授权.Authentication authentication = new UsernamePasswordAuthenticationToken(user, user.getPassword(), userService.getAuthorities(user.getId()));SecurityContextHolder.setContext(new SecurityContextImpl(authentication));}}return true;}
  • 请求结束时也需要清理一下认证
     //最后还需要清理 hostHolder 中的 User(在整个请求结束之后),重写 afterCompletion 方法@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) throws Exception {hostHolder.clear();//请求结束时也需要清理一下认证SecurityContextHolder.clearContext();}

3️⃣退出登录时也清理一下认证(LogicController 类)

    //退出业务方法@RequestMapping(path = "/logout", method = RequestMethod.GET)public String logout(@CookieValue("ticket") String ticket) {userService.logout(ticket);//退出登录时也清理一下认证SecurityContextHolder.clearContext();return "redirect:/login";//默认 GET 请求}

1.4 CSRF 配置

CSRF攻击原理:某网站盗取了你(浏览器)的cookie凭证,模拟你的身份访问服务器,通常利用 表单 提交数据。

防止CSRF攻击原理:Security会在每个表单中生成隐藏的 token,防止CSRF攻击

2.置顶、加精、删除

  • 功能实现:点击指定,修改帖子的类型;点击“加精”、“删除”,修改帖子的状态
  • 权限管理:版主可以执行“置顶”、“加精”操作;管理员可以执行“删除”操作
  • 按钮显示:版主可以看到“置顶”、“加精”按钮;管理员可以看到“删除”按钮

添加依赖:

<dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>

2.1 开发数据访问层

对帖子操作,进行修改帖子:打开 dao 包下的 discussPostMapper.java 类:

  • 添加修改帖子类型、状态方法
    //修改帖子类型int updateType(int id, int type);//修改帖子状态int updateStatues(int id, int status);

打开配置文件(discusspost-mapper.xml)进行添加:

    <!--修改帖子类型--><update id="updateType">update discuss_post set type = #{type} where id = #{id}</update><!--修改帖子状态--><update id="updateStatus">update discuss_post set status = #{status} where id = #{id}</update>

2.2 业务层

在 service 包下的 DiscussPostService 类进行添加:

  • 添加修改帖子类型、状态方法
    //修改帖子类型public int updateType(int id, int type) {return discussPostMapper.updateType(id, type);}//修改帖子状态public int updateStatus(int id, int status) {return discussPostMapper.updateStatus(id, status);}

2.3 表现层

在 controller 包下的 DiscussPostController 类下新添加置顶、加精、删除三个方法

  • 置顶:添加路径,并且置顶需要提交数据,是一个 POST 请求;而且还是一个异步请求,点击置顶按钮之后,不整体刷新,添加 @ResponseBody
  • 添加置顶方法,传入帖子 id:调用 discusssPostService 进行类型修改为1,此时帖子发生了变化,需要把最新的帖子数据进行同步到 elasticsearch 中,确保搜索到最新的帖子,需要触发帖子事件
  • 最终返回成功的提示
  • 加精、删除方法类似;只是删除不需要触发帖子事件,而是触发一个删帖事件,在CommunityConstant.java 中添加删帖主题
    /*** 主题: 删帖*/String TOPIC_DELETE = "delete";
  • 删帖事件是新加事件,没有处理,需要在事件消费者中消费删贴事件(EventConsumer):类似于消费发帖事件
  •     // 消费删帖事件@KafkaListener(topics = {TOPIC_DELETE})public void handleDeleteMessage(ConsumerRecord record) {if (record == null || record.value() == null) {logger.error("消息的内容为空!");return;}Event event = JSONObject.parseObject(record.value().toString(), Event.class);if (event == null) {logger.error("消息格式错误!");return;}elasticsearchService.deleteDiscussPost(event.getEntityId());}
    //置顶//添加路径,并且置顶需要提交数据,是一个 POST 请求;而且还是一个异步请求,点击置顶按钮之后,不整体刷新,添加 @ResponseBody@RequestMapping(path = "/top", method = RequestMethod.POST)@ResponseBodypublic String setTop(int id) {//调用 discusssPostService 进行类型修改为1discussPostService.updateType(id, 1);//此时帖子发生了变化,需要把最新的帖子数据进行同步到 elasticsearch 中,确保搜索到最新的帖子,需要触发帖子事件// 触发发帖事件Event event = new Event().setTopic(TOPIC_PUBLISH).setUserId(hostHolder.getUser().getId())//当前用户.setEntityType(ENTITY_TYPE_POST).setEntityId(id);eventProducer.fireEvent(event);return CommunityUtil.getJSONString(0);}//加精@RequestMapping(path = "/wonderful", method = RequestMethod.POST)@ResponseBodypublic String setWonderful(int id) {discussPostService.updateStatus(id, 1);// 触发发帖事件Event event = new Event().setTopic(TOPIC_PUBLISH).setUserId(hostHolder.getUser().getId()).setEntityType(ENTITY_TYPE_POST).setEntityId(id);eventProducer.fireEvent(event);return CommunityUtil.getJSONString(0);}// 删除@RequestMapping(path = "/delete", method = RequestMethod.POST)@ResponseBodypublic String setDelete(int id) {discussPostService.updateStatus(id, 2);// 触发删帖事件Event event = new Event().setTopic(TOPIC_DELETE).setUserId(hostHolder.getUser().getId()).setEntityType(ENTITY_TYPE_POST).setEntityId(id);eventProducer.fireEvent(event);return CommunityUtil.getJSONString(0);}

前端页面 discuss-detail.html:

                    <div class="float-right"><input type="hidden" id="postId" th:value="${post.id}"><button type="button" class="btn btn-danger btn-sm" id="topBtn"th:disabled="${post.type==1}" sec:authorize="hasAnyAuthority('moderator')">置顶</button><button type="button" class="btn btn-danger btn-sm" id="wonderfulBtn"th:disabled="${post.status==1}" sec:authorize="hasAnyAuthority('moderator')">加精</button><button type="button" class="btn btn-danger btn-sm" id="deleteBtn"th:disabled="${post.status==2}" sec:authorize="hasAnyAuthority('admin')">删除</button></div>

discuss.js:

$(function(){$("#topBtn").click(setTop);$("#wonderfulBtn").click(setWonderful);$("#deleteBtn").click(setDelete);
});function like(btn, entityType, entityId, entityUserId, postId) {$.post(CONTEXT_PATH + "/like",{"entityType":entityType,"entityId":entityId,"entityUserId":entityUserId,"postId":postId},function(data) {data = $.parseJSON(data);if(data.code == 0) {$(btn).children("i").text(data.likeCount);$(btn).children("b").text(data.likeStatus==1?'已赞':"赞");} else {alert(data.msg);}});
}// 置顶
function setTop() {$.post(CONTEXT_PATH + "/discuss/top",{"id":$("#postId").val()},function(data) {data = $.parseJSON(data);if(data.code == 0) {$("#topBtn").attr("disabled", "disabled");} else {alert(data.msg);}});
}// 加精
function setWonderful() {$.post(CONTEXT_PATH + "/discuss/wonderful",{"id":$("#postId").val()},function(data) {data = $.parseJSON(data);if(data.code == 0) {$("#wonderfulBtn").attr("disabled", "disabled");} else {alert(data.msg);}});
}// 删除
function setDelete() {$.post(CONTEXT_PATH + "/discuss/delete",{"id":$("#postId").val()},function(data) {data = $.parseJSON(data);if(data.code == 0) {location.href = CONTEXT_PATH + "/index";} else {alert(data.msg);}});
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/579223.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥杯的学习规划

c语言基础&#xff1a; Python语言基础 学习路径&#xff1a;画框的要着重学习

一文读懂SoBit 跨链桥教程

从BTC网络到Solana网络桥接BRC20 1.打开SoBit平台&#xff1a;在您的网络浏览器中启动SoBit Bridge应用程序。 2.连接您的钱包&#xff1a; 选择SoBit界面右上角的比特币网络来连接您的数字钱包。 3.选择源链、目标链和您想桥接的代币&#xff1a; 从下拉菜单中选择’BTC’作为…

翻硬币C语言

分析&#xff1a;首先&#xff0c;我们如果想要使得两次的硬币可以转化&#xff0c;那么两组字符对应不同的的个数就只能是偶数&#xff0c;比如&#xff1a; * * * * * o o * * * * * 我们要对上面的例子翻动5次&#xff0c;我们可以看出两个不同的位置相差五个单位&#x…

用C/C++实现MSML协议栈的详细介绍

一、MSML协议简介 MSML&#xff08;Media Server Markup Language&#xff09;是一种基于XML的标记语言&#xff0c;用于控制媒体服务器。它是媒体服务器控制协议的一种&#xff0c;允许第三方应用与媒体服务器进行交互&#xff0c;实现对媒体流的创建、修改和释放等操作。MSM…

Netty—Reactor线程模型详解

文章目录 前言线程模型基本介绍线程模型分类Reactor线程模型介绍Netty线程模型&#xff1a; 传统阻塞IO的缺点Reactor线程模型单Reactor单线程模式单Reactor多线程模式主从Reactor多线程Reactor 模式小结 Netty 线程模型案例说明&#xff1a;Netty核心组件简介ChannelPipeline与…

Linux中Mysql数据库备份操作

逻辑备份 备份的是建表、建库、插入等操作所执行SQL语句&#xff0c;适用于中小型数据库&#xff0c;效率相对较低。 本质&#xff1a;导出的是SQL语句文件 优点&#xff1a;不论是什么存储引擎&#xff0c;都可以用mysqldump备成SQL语句 缺点&#xff1a;速度较慢&#xff0c;…

Centos7安装Docker和Docker-Compose

环境 操作系统&#xff1a;Centos 7.9 root环境 Docker安装 卸载原先的Docker环境 如果你先前的操作系统安装了Docker环境&#xff0c;请卸载 Docker 相关的软件包&#xff0c;没有则忽略这一步。 yum remove docker \docker-client \docker-client-latest \docker-common \doc…

(2021|CVPR,XMC-GAN,对比学习,注意力自调制)用于文本到图像生成的跨模态对比学习

Cross-Modal Contrastive Learning for Text-to-Image Generation 公众&#xff1a;EDPJ&#xff08;添加 VX&#xff1a;CV_EDPJ 或直接进 Q 交流群&#xff1a;922230617 获取资料&#xff09; 目录 0. 摘要 1. 简介 2. 相关工作 3. 基础 4. 方法 4.1 用于文本到图像…

【软件工程】可执行文件和数据分离

一、概述 可执行文件和数据分离是一种软件设计策略&#xff0c;旨在将程序代码和程序使用的数据分离存储。这种方法通常用于提高软件的模块化程度和灵活性&#xff0c;以及方便软件的管理和维护。 在可执行文件和数据分离中&#xff0c;程序代码通常以可执行文件的形式存储&a…

Java小案例-Sentinel的实现原理

前言 Sentinel是阿里开源的一款面向分布式、多语言异构化服务架构的流量治理组件。 主要以流量为切入点&#xff0c;从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。 核心概念 要想理解一个新的技…

unityc用vs2017介绍

21版unity能用17vs&#xff0c;只要在unity的Edit/Preferences/ExternalTools里面改既可。

音频修复增强软件iZotope RX 10 mac特点介绍

iZotope RX 10 mac是一款音频修复和增强软件。 iZotope RX 10 mac软件特点 声音修复&#xff1a;iZotope RX 10可以去除不良噪音、杂音、吱吱声等&#xff0c;使音频变得更加清晰干净。 音频增强&#xff1a;iZotope RX 10支持对音频进行音量调节、均衡器、压缩器、限制器等处…

SpringSecurity6 | 登录失败后的JSON处理

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: 循序渐进学SpringSecurity6 ✨特色专栏: MySQL学习 🥭本文内容: SpringSecurity6 | 登录失败后的JSON处理 📚个人知识库: Leo知识库,…

Java架构师系统架构设计实践

目录 1 导语2 架构设计实践本章概述3 架构设计要素概述和规划4 架构设计模式5 架构设计输入6 架构设计输出7 架构设计要素总结 想学习架构师构建流程请跳转&#xff1a;Java架构师系统架构设计 1 导语 Java架构师在进行系统架构设计时&#xff0c;需要综合考虑多个方面&#…

SAP PP 配置学习(二)

MRP 参数文件设定 扩允物料视图 删除物料 物料批量维护

【C Primer Plus第六版 学习笔记】第十四章 结构和其他数据形式

有基础&#xff0c;进阶用&#xff0c;个人查漏补缺 建立结构声明&#xff1a;描述该对象由什么组成&#xff0c;即结构布局 格式&#xff1a; 关键字 标记&#xff08;可选&#xff09;{结构 }&#xff1b; 举例&#xff1a; struct book{char title[2];char author[4];float …

欧洲影像学人工智能和影像组 学文章审稿指南解读(一)--本手稿是否专注于AI/影像组学的报告?

欧洲影像学人工智能和影像组 学文章审稿指南解读 针对人工智能&#xff08;AI&#xff09;和影像组学领域的研究文章&#xff0c;欧洲影像学&#xff08;European Radiology&#xff09;期刊为审稿人提出了一系列特别的说明和要求。这些要求不仅仅是审稿的准则&#xff0c;更是…

Git 查询某段时间所有用户提交的代码量并过滤文件格式

获取代码提交用户 git log --format%aN| sort -u | while read name; do echo -en "$name\t"; done;获取代码提交文件的修改情况 git log --authorforwardhuan --prettytformat: --numstat获取代码提交文件的修改情况(过滤文件类型) git log --authorforwardhuan …

使用TLS/SSL Pinning保护安卓应用程序

使用TLS/SSL Pinning保护安卓应用程序 在现代术语中&#xff0c;“SSL”&#xff08;安全套接层&#xff09;通常指的是“TLS”&#xff08;传输层安全&#xff09;。虽然 SSL 和 TLS 不是同一个东西&#xff0c;但 TLS 是 SSL 的改进和更安全的版本&#xff0c;并且在实践中已…

在k8s中将gitlab-runner的运行pod调度到指定节点

本篇和前面的 基于helm的方式在k8s集群中部署gitlab 具有很强的关联性&#xff0c;因此如果有不明白的地方可以查看往期分享&#xff1a; 基于helm的方式在k8s集群中部署gitlab - 部署基于helm的方式在k8s集群中部署gitlab - 备份恢复基于helm的方式在k8s集群中部署gitlab - 升…