项目1在线交流平台-7.构建安全高效的企业服务-2.使用Security自定义社区网页认证与授权
文章目录
- 功能需求
- 一、 废弃登录检查的拦截器
- 二、授权配置
- 1. 导包
- 2. Security配置
- 2.1 `WebSecurity`
- 2.2`HttpSecurity`
- ` http.authorizeRequests()`
- `.antMatchers()`-要验证的请求路径
- `.hasAnyAuthority( )`-需要的权限
- `.anyRequest().permitAll()`-请他请求无需权限访问
- `http.exceptionHandling()`
- `authenticationEntryPoint`-认证没通过
- `.accessDeniedHandler`-权限不够
- `http.logout()`-退出设置
- 三、使用自定义认证系统
- 1. 处理思路
- 2. 在Service业务层增加查询权限的方法
- 3.认证信息进token和Context时机
- `new UsernamePasswordAuthenticationToken()`-存入token
- `SecurityContextHolder.setContext()`-存入context
- 4. 请求之后和退出登录后要清理容器的认证信息
- `SecurityContextHolder.clearContext()`-清理容器
- 四、处理CSRF攻击配置
- 1. 防止CFRS攻击原理
- 2. Security自动配置了普通请求的CSRF攻击
- 3. 异步请求自己配置csrf的tocken
- HTML `` 标签
- 标签定义及使用说明
- 提示和注释
- 自定义`` 标签,传入csdf元数据
- 在发布请求的js文件中设置-将csrf令牌发给浏览器携带
- 4. 关闭csrf
- `.and().csrf().disable()`- 关闭csrf功能
- 5. 测试结果显示
- 6. 其他异步请求配置
参考牛客网高级项目教程
尚硅谷SpringSecurity教程笔记
社区 Spring Security 从入门到进阶系列教程
功能需求
- 1.有些网页请求需要登录者才有权限访问,采用security权限空值,废弃之前的登录检查拦截器控制
- 2.对社区网页中的不同请求分配不同的访问权限
- 3.登录用户密码验证认证,之前系统中认证逻辑已经很完善了,可以绕过Security默认认证流程,采用自定义的认证逻辑
- 4.CSRF配置更新,原配置只能处理普通的表单请求,对于异步的AJAX表单请求,需要自定义CSRF配置
一、 废弃登录检查的拦截器
- 拦截器配置中,将此拦截器注释掉即可,即不会将拦截器类添加到系统拦截器中去管理
二、授权配置
1. 导包
<!-- 整合security--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>
2. Security配置
- 因使用自定义的认证逻辑,故,无需重写Security的认证方法
AuthenticationManagerBuilder=
2.1 WebSecurity
- 忽略静态资源的访问
@Override
public void configure(WebSecurity web) throws Exception {// 忽略静态资源的访问web.ignoring().antMatchers("/resources/**");
}
2.2HttpSecurity
http.authorizeRequests()
- 处理要验证权限的请求-即访问这些请求,需要有一定的权限
.antMatchers()
-要验证的请求路径
.hasAnyAuthority( )
-需要的权限
.anyRequest().permitAll()
-请他请求无需权限访问
- 注意,用户头像的请求,不能拦截,否则首页中普通访客看不到用户的头像
@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_ADMIN,AUTHORITY_MODERATOR,AUTHORITY_USER).anyRequest().permitAll(); // 请他请求无需权限访问
}
http.exceptionHandling()
- 当访问某些请求,权限不足而出现异常后处理的逻辑
authenticationEntryPoint
-认证没通过
参考博客
他所建模的概念是:“认证入口点”。处理用户请求处理过程中遇到认证异常的接口,
被
ExceptionTranslationFilter
用于开启特定认证方案(authentication schema
)的认证流程。该接口只定义了一个方法 :
@Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { }
- 这里参数
request
是遇到了认证异常authException
用户请求, response
是将要返回给客户的相应,- 方法
commence
实现,也就是相应的认证方案逻辑会修改response
并返回给用户引导用户进入认证流程。
- 这里参数
处理逻辑:参考之前统一处理异常的逻辑
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)) { // 异步请求,向前端用JSON写入错误提示信息response.setContentType("application/plain;charset=utf-8"); // 设置格式:普通字符串PrintWriter writer = response.getWriter();writer.write(CommunityUtil.getJSONString(403, "您还没有登录哦!"));} else { // 普通请求-直接重定向到登录页面response.sendRedirect(request.getContextPath() + "/login");}}})
.accessDeniedHandler
-权限不够
- 处理权限不够的异常接口,接口中也定义了一个处理的逻辑方法
- 在方法中进行处理即可
- 异步请求
- 普通请求
.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");}}
});
定义提示没有权限访问的网页请求
@RequestMapping(path = "/denied", method = RequestMethod.GET) public String getDeniedPage() {return "/error/404"; }
http.logout()
-退出设置
因为Security是过滤器,会在Controller之前拦截默认的
/logout/
请求,就不会走自定义的/logout/
请求故,为了处理自定义的退出逻辑,在配置中,将默认的请求路径改了,就不会去拦截
/logout/
请求
// 退出设置-不走security自定义的退出逻辑,走自定义的退出逻辑
http.logout().logoutUrl("/securitylogout");// 覆盖默认设置的/logout路径,走自己定义的退出逻辑
三、使用自定义认证系统
1. 处理思路
- 前面配置中,没有对认证方法进行重写处理,是因为项目中前期的认证逻辑已经很完善,可以直接使用
- 为了使用自定义的认证逻辑,需要将自己认证逻辑与Security结合
- Security认证处理后,都会将认证信息放入token容器里,每次认证都会从token中取出主体信息进行验证
- 因此,可以将之前自定义的认证处理的结果也设法放进Security的token容器内,再将token放入SecurityContext上下文中
- 就可以让Security走之前定义的认证处理逻辑
- 因为Context上下文对所有请求都是共享的,这样逻辑上可以对所有请求进行认证
2. 在Service业务层增加查询权限的方法
- 自定义的User实体类和Service类可以不用继承
UserDetail
、UserDetailsService
接口,不需要Security管理这个主体 - 但是,需要在自定义的业务层中,添加根据用户id查询指定用户权限的方法,这样才能构建认证信息交给token
/*** 查询指定用户的权限* @param userId 指定用户id* @return 权限集合-有可能一个用户有多个权限*/
public Collection<? extends GrantedAuthority> getAuthorities(int userId) {User user = this.findUserById(userId); // 用service包装的方法-从redis中查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;
}
3.认证信息进token和Context时机
- 之前通过拦截器进行认证登录状态,判断是否登录,
- 是给用户一个ticket,用户访问时,拿到这个ticket,去数据库中查找凭证
- 根据凭证信息认证用户是否是登录状态,如果认证通过,
- 将用户信息存入到当前请求线程容器中,方便每个请求获取使用
- 同样逻辑,认证信息要再存一份放入token里,方便Security进行权限管理
new UsernamePasswordAuthenticationToken()
-存入token
SecurityContextHolder.setContext()
-存入context
// 在Controller之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {logger.debug("preHandle " + handler.toString());// 1.获取ticketString ticket = CookieUtil.getValue(request, "ticket");// 2.根据ticket查询userif(ticket != null) {// 向数据库中查询凭证LoginTicket loginTicket = userService.selectByTicket(ticket);if(loginTicket != null &&loginTicket.getStatus() == 0 &&loginTicket.getExpired().after(new Date())) {// 有效,就根据凭证找到用户信息User user = userService.findUserById(loginTicket.getUserId());// 在本次请求中持有用户信息,在请求结束前一直保存在请求的当前线程容器中hostHolder.setUsers(user);// 将认证信息-认证主体及主体的权限在存一份到Security的token中Authentication authentication = new UsernamePasswordAuthenticationToken(user, user.getPassword(), userService.getAuthorities(user.getId()));// 再将认证信息存入SecurityContext中SecurityContextHolder.setContext(new SecurityContextImpl(authentication));}}return true; // true,表示拦截处理之后继续执行
}
4. 请求之后和退出登录后要清理容器的认证信息
SecurityContextHolder.clearContext()
-清理容器
请求之后清理
// 在模板视图渲染后处理,一般做清理工作
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {logger.debug("afterHandle " + handler.toString());hostHolder.clear();SecurityContextHolder.clearContext();
}
退出登录后清理
/*** 退出登录请求* @param ticket 登录凭证t票* @return*/
@RequestMapping(path = "/logout", method = RequestMethod.GET)
public String logout(@CookieValue("ticket") String ticket) {userService.logout(ticket);SecurityContextHolder.clearContext();return "redirect:/login";
}
四、处理CSRF攻击配置
Web安全攻击XSS与CSRF攻击简述
配置CFRS(盗取cooike中的凭证,模仿你的身份提交表单,盗取你的数据)
普通请求SpringSecurity已经自定义配置好了,异步的需要自己处理
1. 防止CFRS攻击原理
CSRF俗称跨站协议伪造(跨站请求攻击)
过程模拟:
- 即A访问表单,在提交表单前,访问了B网站
- B窃取了A的cookie,伪造成A的身份向服务器提交了表单form,窃取数据
防止攻击策略
- 服务器向信任的浏览器发放一个随机的tocken放在提交的表单form中
- 浏览器提交表单时,会连同tocken一起提交给服务器去验证
- 其他网站就算盗用了ticket,但无法知道表单中的tocken,故,会被服务器拒绝访问
2. Security自动配置了普通请求的CSRF攻击
- 点开帖子详情页面,里面有很多提交表单请求
- 查看源代码,会发现隐藏的标签里传入了CSRF的tocken随机值
3. 异步请求自己配置csrf的tocken
HTML <meta>
标签
标签定义及使用说明
- 元数据(Metadata)是数据的数据信息。
- 标签提供了 HTML 文档的元数据。元数据不会显示在客户端,但是会被浏览器解析。
提示和注释
注意:<meta>
标签通常位于<head>
区域内。
注意: 元数据通常以 名称/值 对出现。
注意: 如果没有提供 name 属性,那么名称/值对中的名称会采用 http-equiv 属性的值。
自定义<meta>
标签,传入csdf元数据
- 访问该页面时,在此生成CSRF令牌,将Security中的元数据的传给页面,以key,value形式
<!-- 访问该页面时,在此生成CSRF令牌,将Security中的元数据的传给页面,以key,value形式--><!--csrf的key--><meta name="_csrf" th:content="${_csrf.headerName}"><!--csrf的value--><meta name="_csrf_header" th:content="${_csrf.token}">
在发布请求的js文件中设置-将csrf令牌发给浏览器携带
// 发送AJAX请求之前,将CSRF令牌设置到请求的消息头中.
// 使用JQuery选择器,取指定meta标签中的content属性的值,赋值给变量key,value
var header = $("meta[name='_csrf_header']").attr("content");
var token = $("meta[name='_csrf']").attr("content");
// 在发布异步请求前,先对请求进行设置
$(document).ajaxSend(function (e, xhr, options) {// xhr为发布异步请求的核心对象,设置请求头的key和value-设置完后可以放到浏览器中// 发送请求时,会携带这个数据xhr.setRequestHeader(header, token);
});
4. 关闭csrf
.and().csrf().disable()
- 关闭csrf功能
5. 测试结果显示
访问首页,mata标签携带数据
发布异步请求
6. 其他异步请求配置
- 同理,点赞、关注、取关、发送私信这些异步请求也要配置csrf,否则无法访问
点赞:
- 帖子详情页面
discuss.js
关注与取关
- 个人主页页面
- 关注和取关页面
profile.js
发送私信
- 私信页面
letter.html
- 和私信详情页面
letter.js
项目1在线交流平台-7.构建安全高效的企业服务-2.使用Security自定义社区网页认证与授权相关推荐
- 项目1在线交流平台-7.构建安全高效的企业服务-3. Security整合Kafka,ES,Thymeleaf实例-对帖子置顶、加精、删除
文章目录 功能需求 一.置顶.加精.删除帖子功能的实现 1. dao层处理数据 接口定义 sal语句定义 2. service层业务处理 3. Controller层处理按钮事件异步请求 异步请求及k ...
- 第七章 项目进阶,构建安全高效的企业服务
第七章 项目进阶,构建安全高效的企业服务 1.Spring Security 1.1 基本介绍 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案 ...
- 【仿牛客网笔记】项目进阶,构建安全高效的企业服务——热帖排行
p:投票数 T:发布时间间隔 G:系数,通常为1.5,1.8 计算帖子的分数 注入RedisTemplate 帖子刷新 实现定时任务 刷新帖子 实现更新帖子分数 刷新帖子分数任务 配置Trigger ...
- 如何构建一个高效的企业舆情监测系统?
随着互联网的快速发展,企业需要对自身在网络上的形象和声誉进行全方位的监控和管理,以保证企业的长期稳定发展.构建一个高效的企业舆情监测系统已经成为了当下企业发展的必要手段.本文将通过国内具体案例分析,阐 ...
- 项目1在线交流平台-6.Elasticsearch分布式搜索引擎-3.ES结合Kafka应用-开发社区搜索功能
文章目录 功能需求 一.Service层处理操作ES服务器的数据 二.Controller层处理帖子添加和评论事件请求 1.添加帖子时-触发事件-发布消息 2. 添加评论时-触发发帖事件-发布消息 三 ...
- 项目1在线交流平台-4. 使用radis高性能储存方案-1.redis入门-特点、安装与支持数据类型
文章目录 1.为何使用redis 1.1 NoSQL NoSQL数据库特点 常见NoSQL数据库 1.2 什么是redis,有何特点 1.3 为何选用redis 2. redis的安装与使用 2.1 ...
- 项目1在线交流平台-4. 使用radis高性能储存方案-5.redis常用使用场景-开发关注功能-redis事务
文章目录 功能需求 1. dao层设计redis对应的key 设计储存关注对象信息的健值对 key value 设计储存粉丝信息的健值对 key value 2. Service层处理关注和取关的业务 ...
- 项目1在线交流平台-4. 使用radis高性能储存方案-3.redis使用场景-点赞功能
文章目录 功能需求 1. dao层设计redis的key生成工具 2. Service层处理点赞业务 添加赞或删除赞 ==isMember(key, userId)== ==remove(key, u ...
- 项目1在线交流平台-3.开发交流社区核心功能模块-7.显示私信信息
文章目录 功能需求及处理策略 1. dao层处理会话私信数据 数据库设计 接口方法声明,sql编写 接口方法 sql-子查询语句 组函数与分组查询 根据分组结果进行子查询 测试 2.service层业 ...
最新文章
- 2022-2028年中国卫星互联网产业深度调研及投资前景预测报告(全卷)
- 看腾讯运维应对“18岁照片全民怀旧”事件的方案,你一定不后悔!
- 4 相机切换_新手必读 | iPhone手机的自带相机操作完全指南
- TypeScript入门教程 之 for ... of 与 for ... in
- python 闭包中引用的变量值变更问题
- ExtJs页面布局总结(转载)
- geexbox 编译
- bzoj3223Tyvj 1729 文艺平衡树 splay
- oracle instant client卸载,Oracle Instant Client(即时客户端) 安装与配置
- 各类图像数据大集合(下载链接)
- 饥荒服务器物品指令,饥荒控制台指令大全物品大全服务器管理命令大全.docx
- 单片机控制光耦开关继而控制电机转动
- python excel 饼图 简书_Python实现绘画多个饼图
- 嵌入式linux学习路线参考(LINUX学习者必看经典)
- 自媒体剪辑视频都在用的6个音效素材网站。
- 学习笔记:WEB安全防护
- Gateway篇(一)之Gateway配置
- 剖析 Pexpect
- 苹果手机投影_会议室投影机最常用的一款无线投影解决方案,操作便捷,运行稳定且不需要装软件...
- BZOJ4810 [Ynoi2017]由乃的玉米田
热门文章
- Tomcat 9下载安装及配置
- 多线程的终止方法(停止线程)
- Nestjs模块机制的概念和实现原理
- springboot 中如何使用 ingest-attachment
- linux脚本简介,Linux Shell脚本简介
- Activiti 从入门到精通
- ppt嵌入文件对计算机有危害,有必要将背景音乐嵌入到PPT文档中吗?没有嵌入PPT,换电脑就无法播放了?-文件夹背景...
- 当遇到error: stray ‘\241‘ in program错误的解决方法
- 代谢组学助力研究“线粒体闪烁”,揭示细胞“返老还童”的新奥秘
- python 数据分析入门教程(三)_Python数据分析入门教程(三):数据选择——AIU人工智能学院-经管之家官网!...