8.Spring Security 权限控制
1.简介入门
JavaEE和SpringMVC :Spring Security就是通过11个Fliter进行组合管理

小Demo
user实体类
user.type字段,0普通用户,1超级管理员,2版主
补全get set tostring
implement UserDetails,重写以下方法
// true: 账号未过期.
@Override
public boolean isAccountNonExpired() {return true;
}// true: 账号未锁定.
@Override
public boolean isAccountNonLocked() {return true;
}// true: 凭证未过期.
@Override
public boolean isCredentialsNonExpired() {return true;
}// true: 账号可用.
@Override
public boolean isEnabled() {return true;
}//获取当前用户权限列表
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {List<GrantedAuthority> list = new ArrayList<>();list.add(new GrantedAuthority() {@Overridepublic String getAuthority() {switch (type) {case 1:return "ADMIN";default:return "USER";}}});return list;
}
UserService
unservice implement UserDetailsService
重写方法loadUserByUsername
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {return this.findUserByName(username);
}
SecurityConfig 配置类
extend父类WebSecurityConfigurerAdapter
注入UserService
重写configure(WebSecurity web),忽略静态资源的访
@Override
public void configure(WebSecurity web) throws Exception {// 忽略静态资源的访问web.ignoring().antMatchers("/resources/**");
}
重写configure(AuthenticationManagerBuilder auth),这个方法主要是做认证。
AuthenticationManager: 认证的核心接口
AuthenticationManagerBuilder: 用于构建AuthenticationManager对象的工具
ProviderManager: AuthenticationManager接口的默认实现类
ProviderManager--一组-->AuthenticationProvider--每个负责-->一种认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {// 内置的认证规则// auth.userDetailsService(userService).passwordEncoder(new Pbkdf2PasswordEncoder("12345"));// 自定义认证规则// AuthenticationProvider: ProviderManager持有一组AuthenticationProvider,每个AuthenticationProvider负责一种认证.// 委托模式: ProviderManager将认证委托给AuthenticationProvider.auth.authenticationProvider(new AuthenticationProvider() {// Authentication: 用于封装认证信息的接口,不同的实现类代表不同类型的认证信息.@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {String username = authentication.getName();String password = (String) authentication.getCredentials();User user = userService.findUserByName(username);if (user == null) {throw new UsernameNotFoundException("账号不存在!");}password = CommunityUtil.md5(password + user.getSalt());if (!user.getPassword().equals(password)) {throw new BadCredentialsException("密码不正确!");}// principal: 主要信息; credentials: 证书; authorities: 权限;return new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());}// 当前的AuthenticationProvider支持哪种类型的认证.@Overridepublic boolean supports(Class<?> aClass) {// UsernamePasswordAuthenticationToken: Authentication接口的常用的实现类.return UsernamePasswordAuthenticationToken.class.equals(aClass);}});
}
重写configure(HttpSecurity http)
配置登陆页面http.formLogin()
登录成功处理器.successHandler
登录失败处理器.failureHandler
退出相关配置http.logout()
授权配置http.authorizeRequests()
验证码在验证账号之前
@Override
protected void configure(HttpSecurity http) throws Exception {// 登录相关配置http.formLogin().loginPage("/loginpage").loginProcessingUrl("/login").successHandler(new AuthenticationSuccessHandler() {@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {response.sendRedirect(request.getContextPath() + "/index");}}).failureHandler(new AuthenticationFailureHandler() {@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {request.setAttribute("error", e.getMessage());request.getRequestDispatcher("/loginpage").forward(request, response);}});// 退出相关配置http.logout().logoutUrl("/logout").logoutSuccessHandler(new LogoutSuccessHandler() {@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {response.sendRedirect(request.getContextPath() + "/index");}});// 授权配置http.authorizeRequests().antMatchers("/letter").hasAnyAuthority("USER", "ADMIN").antMatchers("/admin").hasAnyAuthority("ADMIN").and().exceptionHandling().accessDeniedPage("/denied");// 增加Filter,处理验证码http.addFilterBefore(new Filter() {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;if (request.getServletPath().equals("/login")) {String verifyCode = request.getParameter("verifyCode");if (verifyCode == null || !verifyCode.equalsIgnoreCase("1234")) {request.setAttribute("error", "验证码错误!");request.getRequestDispatcher("/loginpage").forward(request, response);//转发return;}}// 让请求继续向下执行.filterChain.doFilter(request, response);}}, UsernamePasswordAuthenticationFilter.class);// 记住我http.rememberMe().tokenRepository(new InMemoryTokenRepositoryImpl()).tokenValiditySeconds(3600 * 24).userDetailsService(userService);}
HomeController
在首页添加欢迎信息,通过SecurityContextHolder获取登陆者信息
@RequestMapping(path = "/index", method = RequestMethod.GET)
public String getIndexPage(Model model) {// 认证成功后,结果会通过SecurityContextHolder存入SecurityContext中.Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();if (obj instanceof User) {model.addAttribute("loginUser", obj);}return "/index";
}
2.权限控制

废除原有的拦截器
config.WebMvcConfig 种注释掉两部分
@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginTicketInterceptor).excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");// registry.addInterceptor(loginRequiredInterceptor)// .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");registry.addInterceptor(messageInterceptor).excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");}
配置授权SecurityConfig
重写configure(HttpSecurity http),忽略对静态资源的拦截
重写configure(HttpSecurity http),http.authorizeRequests()进行授权
.antMatchers:登陆后可访问路径
hasAnyAuthority:可以访问的权限
.anyRequest().permitAll():其他请求都允许
http.exceptionHandling():越权行为发生时
覆盖它默认的logout逻辑,才能执行我们自己的退出代码
@Override
protected 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()// 权限不够时的处理http.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {// 没有登录@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) 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() + "/login");}}}).accessDeniedHandler(new AccessDeniedHandler() {// 权限不足@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) 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");
}
UserService增加用户权限
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;
}
修改LoginTicketInterceptor
BUG: 登陆后点击其他需要授权的页面 依然会跳转到登录页面?
问题:在 afterCompletion 里
@RequestMapping(path = "/logout", method = RequestMethod.GET)
public String logout(@CookieValue("ticket") String ticket) {
userService.logout(ticket);
SecurityContextHolder.clearContext();
return "redirect:/login";
}
原因:security包括认证和授权,授权是根据认证的结果来进行的。
这里我们没有使用框架的认证,而采用自己的认证:其实就是保存一下用户的权限,就是将user信息存入 ThreadLocal 和
SecurityContextHolder.setContext。
第一次login之后,已经经过interceptor了,请求处理完后 会执行SecurityContextHolder.clearContext() ,会清除user信息。下次再访问有权限的路径,就需要认证,但此时还没有用户信息,所以需要登录。
preHandle
将得到的结果存入SecurityContext
@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//从cookie获取 ticket封装好cookieString ticket = CookieUtil.getValue(request,"ticket"); //登录时 返回了Cookie cookie = new Cookie("ticket",map.get("tivket").toString());if(ticket != null){//查询 登陆凭证ticketLoginTicket loginTicket = userService.findLoginTicket(ticket);//判断是否有效+否过期if(loginTicket != null &&loginTicket.getStatus() == 0 &&loginTicket.getExpired().after(new Date())){//根据 loginTicket 查询用户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));System.out.println(SecurityContextHolder.getContext());}}return true;}
LoginController
@RequestMapping(path = "/logout", method = RequestMethod.GET)
public String logout(@CookieValue("ticket") String ticket) {userService.logout(ticket);SecurityContextHolder.clearContext();return "redirect:/login";
}
3.加精 置顶 删除

DAO:DiscussPostMapper添加 修改类型 状态 的方法
完善对应的Mapper.xml
DiscussPostController
置顶请求
@RequestMapping(path = "/top", method = RequestMethod.POST)@ResponseBodypublic String setTop(int id){DiscussPost discussPostById = discussPostService.findDiscussPostById(id);// 获取置顶状态,1为置顶,0为正常状态,1^1=0 0^1=1int type = discussPostById.getType()^1;discussPostService.updateType(id, type);// 返回的结果Map<String, Object> map = new HashMap<>();map.put("type", type);// 触发发帖事件(更改帖子状态)Event event = new Event().setTopic(TOPIC_PUBLISH).setUserId(hostHolder.getUser().getId()).setEntityType(ENTITY_TYPE_POST).setEntityId(id);eventProducer.fireEvent(event);return CommunityUtil.getJSONString(0, null, map);}
加精类似
删除 直接将status 改为 2(拉黑 不显示)
配置 SecurityConfig 权限情况
.antMatchers("/discuss/top","/discuss/wonderful").hasAnyAuthority(AUTHORITY_MODERATOR).antMatchers("/discuss/delete","/data/**").hasAnyAuthority(AUTHORITY_ADMIN).anyRequest().permitAll().and().csrf().disable();
相关文章:

8.Spring Security 权限控制
1.简介入门JavaEE和SpringMVC :Spring Security就是通过11个Fliter进行组合管理小Demouser实体类user.type字段,0普通用户,1超级管理员,2版主补全get set tostringimplement UserDetails,重写以下方法// true: 账号未过…...
curl / python+selenium爬取网页信息
Python爬取网页信息 需求: 持续爬取某嵌入式设备配置网页上的状态信息 shell脚本 简单快速, 不用装插件只能爬取静态内容 用curl命令返回整个网页的内容用grep命令抓取其中某些字段结合正则表达式可多样查找但对于动态内容, 比如对某嵌入式设备配置网页上的一条不断更新的信…...

晶体塑性有限元 Abaqus 三维泰森多边形(voronoi模型)插件 V7.0
1 上一版本完整功能介绍: Voronoi晶体插件-6.0版本[新功能介绍] 晶体塑性有限元 Abaqus 三维泰森多边形(voronoi模型)插件 V6.0 2 新增功能模块 7.0版本新增功能模块包括:柱状晶体模块和分层晶体模块。 2.1 二维柱状晶体模块 …...

CPython解释器性能分析与优化
原文来自微信公众号“编程语言Lab”:CPython 解释器性能分析与优化 搜索关注 “编程语言Lab”公众号(HW-PLLab)获取更多技术内容! 欢迎加入 编程语言社区 SIG-元编程 参与交流讨论(加入方式:添加文末小助手…...

Linux 进程:理解进程和pcb
目录一、进程的概念二、CPU分时机制三、并发与并行1.并发2.并行四、pcb的概念一、进程的概念 什么是进程? 进程就是进行中的程序,即运行中的应用程序。比如:电脑上打开的LOL、QQ…… 这些都是一个个的进程。 什么是应用程序? 应用…...

银行数字化转型导师坚鹏:招商银行数字化转型战略研究
招商银行数字化转型战略研究课程背景: 很多银行存在以下问题:不清楚如何制定银行数字化转型战略?不知道其它银行的数字化转型战略是如何演变的? 课程特色:用实战案例解读招商银行数字化转型战略。用独特视角解…...

java 一文讲透面向对象 (20万字博文)
目录 一、前言 二、面向对象程序设计介绍 1.oop三大特性 : 2.面向对象和面向过程的区别 : 3.面向对象思想特点 : 4.面向对象的程序开发 : 三、Java——类与对象 1.java中如何描述一个事物? 2.什么是类? 3.类的五大成员: 4.封装的前提——抽象 : 5.什么是对…...

使用file-selector-button美化原生文件上传
前言 你平时见到的上传文件是下面这样的? 还是下面这种美化过的button样式 还是下面这种复杂的上传组件。 <input type="file" >:只要指定的是type类型的input,打开浏览器就是上面第一种原生的浏览器默认的很丑的样式。 下面的两种是我从ElementUI截的图,…...

0402换元积分法-不定积分
文章目录1 第一类换元法1.1 定理11.2 例题1.2 常见凑微分形式1.2.1常见基本的导数公式的逆运算1.2.2被积函数含有三角函数2 第二类换元法2.1 定理22.2 常见第二换元代换方法2.2.1 三角代换-弦代换2.2.2 三角代换-切代换2.2.3 三角代换-割代换2.2.4 三角代换汇总2.2.5 倒代换2.2…...

信号类型(雷达)——脉冲雷达(三)
系列文章目录 《信号类型(雷达通信)》 《信号类型(雷达)——雷达波形认识(一)》 《信号类型(雷达)——连续波雷达(二)》 文章目录 前言 一、相参雷达 1…...

并查集(13张图解)--擒贼先擒王
目录 前言 故事 🌼思路 🌼总结 🌼代码 👊观察过程代码 👊正确代码 👊细节代码 来自《啊哈算法》 前言 刚学了树在优先队列中的应用--堆的实现 那么树还有哪些神奇的用法呢?我们从一…...

Flutter3引用原生播放器-IOS(Swift)篇
前言由于Flutter项目中需要使用到播放器功能,因此对flutter中各种播放器解决方案进行了一番研究和比对,最后决定还是自己通过Plugin的方法去引用原生播放器符合自己的需求,本篇文章会对各种解决方案做一个简单的比较,以及讲解一下…...

【蓝桥杯每日一题】双指针算法
🍎 博客主页:🌙披星戴月的贾维斯 🍎 欢迎关注:👍点赞🍃收藏🔥留言 🍇系列专栏:🌙 蓝桥杯 🌙我与杀戮之中绽放,亦如黎明的花…...

拼数(一般贪心)
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网 题号:NC16783 时间限制:C/C 1秒,其他语言2秒 空间限制:C/C 262144K,其他语言524288K 64bit IO Format: %lld 题目描述 设有n个正整…...
LeetCode 热题 C++ 169. 多数元素 10. 正则表达式匹配 155. 最小栈
力扣169 给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 输入:nums [3,2,3] 输出࿱…...

Clickhouse学习:MergeTree
MergeTree一、MergeTree逻辑存储结构二、MergeTree物理存储结构三、总结一、MergeTree逻辑存储结构 如上图所示,在排序键(CountrID、Date)上做索引,数据会按照这两个字段先后排序ClickHouse是稀疏索引,每隔8192行做一个索引,如(a,1),(a,2),比如想查a,要读取[0,3)之间的内容,稀疏…...

【java基础】包装类,自动装箱和自动拆箱
文章目录基本介绍包装类自动装箱自动拆箱包装类注意事项包装类比较包装器内容不可变基本介绍 有时,需要将int这样的基本类型转换为对象。所有的基本类型都有一个与之对应的类。 例如,Integer类对应基本类型int。通常,这些类称为包装器&#…...

Android笔记(二十五):两种sdk热更插件资源加载方案
背景 在研究sdk插件化热更新方式的过程中总结出了两套插件资源加载方案,在此记录下 资源热更方式 方式一:合并所有插件资源 需要解决资源id冲突问题 资源ID值一共4个字段,由三部分组成:PackageIdTypeIdEntryId PackageId&…...

spring框架--全面详解(学习笔记)
目录 1.Spring是什么 2.Spring 框架特点 3.Spring体系结构 4.Spring开发环境搭建 5.spring中IOC和DI 6.Spring中bean的生命周期 7.Spring Bean作用域 8.spring注解开发 9.Spring框架中AOP(Aspect Oriented Programming) 10.AOP 实现分类 11.A…...
2023年CDGA考试模拟题库(401-500)
2023年CDGA考试模拟题库(401-500) 401.数据管理战略的SMART原则指的是哪项? [1分] A.具体 、高质量、可操作 、现实、有时间限制 B.具体、可衡量、可检验、现实、有时间限制 C.具体、可衡量、可操作、现实、有时间限制 D.具体、高质量、可检验、现实12-24个月的目标 答…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...

19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...

SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...