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个月的目标 答…...

软件设计师备考文档
cpu 计算机的基本硬件系统:运算器、控制器、存储器、输入设备、输出设备 cpu负责获取程序指令,对指令进行译码并加以执行 * cpu的功能控制器(保证程序正常执行,处理异常事件) 程序控制操作控制运算器(只能…...

Javascript的API基本内容(一)
一、获取DOM对象 querySelector 满足条件的第一个元素 querySelectorAll 满足条件的元素集合 返回伪数组 document.getElementById 专门获取元素类型节点,根据标签的 id 属性查找 二、操作元素内容 通过修改 DOM 的文本内容,动态改变网页的内容。 inn…...

10、最小公倍数
法一: #include <stdio.h>int main(){int a,b;scanf("%d%d",&a,&b);int m a>b?a:b;//m表示a,b之间的较大值while(1){if(m%a0&&m%b0){break;}m;}printf("%d",m);return 0; }法二:a*i%b0成立 #include &…...

【vue】vue2.x项目中使用md文件
一、Vue项目展示md文件的三种方式 1、将md文件 导入为 html 生成的标题标签自带具有id属性,值为标题内容; <h2 id"测试">测试</h2> # 处理md为html字符串 yarn add markdown-loader # 处理字符串,用于导出显示 yarn a…...

操作系统权限提升(十三)之绕过UAC提权-MSF和CS绕过UAC提权
系列文章 操作系统权限提升(十二)之绕过UAC提权-Windows UAC概述 注:阅读本编文章前,请先阅读系列文章,以免造成看不懂的情况!! MSF和CS绕过UAC提权 CS绕过UAC提权 拿到一个普通管理员的SHELL,在CS中没有*号代表有…...

快速排序+快速定位
快速排序算法采用了分治法以及递归作为解决问题的思想。在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以…...

nginx http rewrite module 详解
大家好,我是 17。 今天和大家聊聊 nginx http rewrite module 。 简单来说, ngx_http_rewrite_module module 用正则匹配请求,改写请求,然后做跳转。可以是内部跳转,也可以是外部跳转。 学习这个模块的时候…...

机器学习可解释性一(LIME)
随着深度学习的发展,越来越多的模型诞生,并且在训练集和测试集上的表现甚至于高于人类,但是深度学习一直被认为是一个黑盒模型,我们通俗的认为,经过训练后,机器学习到了数据中的特征,进而可以正…...

CV学习笔记-MobileNet
MobileNet 文章目录MobileNet1. MobileNet概述2. 深度可分离卷积(depthwise separable convolution)2.1 深度可分离卷积通俗理解2.2 深度可分离卷积对于参数的优化3. MobileNet网络结构4. 代码实现4.1 卷积块4.2 深度可分离卷积块4.3 MobileNet定义4.4 完…...

C++进阶——继承
C进阶——继承 1.继承的概念及定义 面向对象三大特性:封装、继承、多态。 概念: 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性的基础上进行扩展,增加功能,这…...