文章目录
- 功能需求
- 一、 废弃登录检查的拦截器
- 二、授权配置
- 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. 导包
<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() {
@Override
public 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
-权限不够
- 处理权限不够的异常接口,接口中也定义了一个处理的逻辑方法
- 在方法中进行处理即可
.accessDeniedHandler(new AccessDeniedHandler() {
@Override
public 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");
}
}
});
http.logout()
-退出设置
http.logout()
.logoutUrl("/securitylogout");
三、使用自定义认证系统
1. 处理思路
- 前面配置中,没有对认证方法进行重写处理,是因为项目中前期的认证逻辑已经很完善,可以直接使用
- 为了使用自定义的认证逻辑,需要将自己认证逻辑与Security结合
- Security认证处理后,都会将认证信息放入token容器里,每次认证都会从token中取出主体信息进行验证
- 因此,可以将之前自定义的认证处理的结果也设法放进Security的token容器内,再将token放入SecurityContext上下文中
- 就可以让Security走之前定义的认证处理逻辑
- 因为Context上下文对所有请求都是共享的,这样逻辑上可以对所有请求进行认证
2. 在Service业务层增加查询权限的方法
- 自定义的User实体类和Service类可以不用继承
UserDetail
、UserDetailsService
接口,不需要Security管理这个主体 - 但是,需要在自定义的业务层中,添加根据用户id查询指定用户权限的方法,这样才能构建认证信息交给token
public Collection<? extends GrantedAuthority> getAuthorities(int userId) {
User user = this.findUserById(userId);
List<GrantedAuthority> list = new ArrayList<>();
list.add(new GrantedAuthority() {
@Override
public 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
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.debug("preHandle " + handler.toString());
String ticket = CookieUtil.getValue(request, "ticket");
if(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);
Authentication authentication = new UsernamePasswordAuthenticationToken
(user, user.getPassword(), userService.getAuthorities(user.getId()));
SecurityContextHolder.setContext(new SecurityContextImpl(authentication));
}
}
return 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();
}
退出登录后清理
@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形式
<meta name="_csrf" th:content="${_csrf.headerName}">
<meta name="_csrf_header" th:content="${_csrf.token}">
在发布请求的js文件中设置-将csrf令牌发给浏览器携带
var header = $("meta[name='_csrf_header']").attr("content");
var token = $("meta[name='_csrf']").attr("content");
$(document).ajaxSend(function (e, xhr, options) {
xhr.setRequestHeader(header, token);
});
4. 关闭csrf
.and().csrf().disable()
- 关闭csrf功能
5. 测试结果显示
-
访问首页,mata标签携带数据
-
发布异步请求
6. 其他异步请求配置
- 同理,点赞、关注、取关、发送私信这些异步请求也要配置csrf,否则无法访问
点赞:
关注与取关
发送私信
- 私信页面
letter.html
- 和私信详情页面
letter.js
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)