发布于2021-05-29 21:56 阅读(1337) 评论(0) 点赞(4) 收藏(3)
io.undertow.servlet.core.ManagedFilter:42
io.undertow.servlet.handlers.FilterHandler:58
org.springframework.web.filter.OncePerRequestFilter:42
org.apache.catalina.core.ApplicationFilterChain
org.springframework.security.web.savedrequest.HttpSessionRequestCache
org.springframework.web.filter.OncePerRequestFilter:请求只会执行一次过滤链,请求转发会跳过,重定向可以被拦截。
GenericFilterBean:请求转发还是会被过滤器拦截,重定向也会被拦截
org.springframework.security.web.access.ExceptionTranslationFilter继承GenericFilterBean
这里会拦截到acess deny异常
不同的exception进行不同的处理,大致可以分成两种Exception。
AuthenticationException
AccessDeniedException
将封装后的请求进行封装,存到session里面key为(SPRING_SECURITY_SAVED_REQUEST)
根据请求匹配入口类org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
org.springframework.security.web.access.intercept.FilterSecurityInterceptor拦截到请求,获取SecurityContextHolder.getContext().getAuthentication(),然后进行投票器AffirmativeBased进行投票管理。鉴权,鉴权成功则放行,鉴权不通过则返回AccessDeniedException,然后跳转到登录页面。
三个决策器的区别如下:
AffirmativeBased:有一个投票器同意了,就通过。
ConsensusBased:多数投票器同意就通过,平局的话,则看 allowIfEqualGrantedDeniedDecisions 参数的取值。
UnanimousBased 所有投票器都同意,请求才通过。
org.springframework.security.access.intercept.AbstractSecurityInterceptor从org.springframework.security.web.access.intercept.FilterSecurityInterceptor中获取securityMetadataSource
WebExpressionVoter进行鉴权。
org.springframework.security.access.expression.SecurityExpressionRoot
org.springframework.security.web.context.SecurityContextPersistenceFilter每次访问时会从session获取key为SPRING_SECURITY_CONTEXT的登录信息,如果能够获取到,则保存到SecurityContextHolder
所谓session可以这样理解:当与服务端进行会话时,比如说登陆成功后,服务端会为用户开壁一块内存区间,用以存放用户这次会话的一些内容,比如说用户名之类的。那么就需要一个东西来标志这个内存区间是你的而不是别人的,这个东西就是session id(jsessionid只是tomcat中对session id的叫法,在其它容器里面,不一定就是叫jsessionid了。),而这个内存区间你可以理解为session。
然后,服务器会将这个session id发回给你的浏览器,放入你的浏览器的cookies中(这个cookies是内存cookies,跟一般的不一样,它会随着浏览器的关闭而消失)。
之后,只有你浏览器没有关闭,你每向服务器发请求,服务器就会从你发送过来的cookies中拿出这个session id,然后根据这个session id到相应的内存中取你之前存放的数据。
但是,如果你退出登陆了,服务器会清掉属于你的内存区域,所以你再登的话,会产生一个新的session了。
这是一个保险措施 因为Session默认是需要Cookie支持的,但有些客户浏览器是关闭Cookie的【而jsessionid是存储在Cookie中的,
如果禁用Cookie的话,也就是说服务器那边得不到jsessionid,这样也就没法根据jsessionid获得对应的session了,获得不了session就
得不到session中存储的数据了。】这个时候就需要在URL中指定服务器上的session标识,也
就是类似于“jsessionid=5F4771183629C9834F8382E23BE13C4C” 这种格式。用一个方法(忘了方法的名字)处理URL串就可以得到
这个东西,这个方法会判断你的浏览器是否开启了Cookie,如果他认为应该加他就会加上去。
jsessionid是服务器那边生成的,因为cookie是服务器那边送到客户端的信息。不管能不能修改jsessionid,都不应该修改,如果你修改了,这就失去了jessionid的自身意义了,你修改的话,你让服务器那边如何找到对应的session?找不到的话,你存放在那个session中的数据不是取不到了吗?
org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter:从cookie中读取remember-me的cookie,读取用户信息。
org.springframework.security.web.session.SessionManagementFilter:存储用户信息到session中。
通过FilterChainProxy进行添加。
在启动时添加SpringSecurity的额外过滤链。
启动查看org.springframework.security.config.annotation.web.builders.HttpSecurity
或者运行时任意请求可以查看FilterChainProxy
例:http://localhost:8088/?userName=admin
直接以admin的角色进入系统
首先自定义过滤器
package com.codermy.myspringsecurityplus.security.filter;
import cn.hutool.core.util.StrUtil;
import com.codermy.myspringsecurityplus.security.UserDetailsServiceImpl;
import com.codermy.myspringsecurityplus.security.dto.JwtUserDto;
import com.codermy.myspringsecurityplus.security.utils.com.pigic.hzeropigic.utils.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: 潘顾昌
* @Date: 2021/5/26 20:16
*/
@Slf4j
public class MyPermitLoginFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
// 获取授权信息
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication!=null){
// 如果已经授权则跳过
filterChain.doFilter(httpServletRequest, httpServletResponse);
return ;
}
try {
// 如果未授权查询是否有请求参数userName
String userName = httpServletRequest.getParameter("userName");
if (StrUtil.isNotEmpty(userName)){
// 如果userName不为空,获取用户信息
UserDetailsServiceImpl userDetailsService = SpringUtils.getBean(UserDetailsServiceImpl.class);
JwtUserDto jwtUserDto = userDetailsService.loadUserByUsername(userName);
// 设置授权信息到SecurityContext
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(jwtUserDto, null, jwtUserDto.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}else {
log.info("未获取到用户信息!");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
}
设置过滤器的位置在UsernamePasswordAuthenticationFilter之后
/**
* anyRequest | 匹配所有请求路径
* access | SpringEl表达式结果为true时可以访问
* anonymous | 匿名可以访问
* denyAll | 用户不能访问
* fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录)
* hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问
* hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问
* hasAuthority | 如果有参数,参数表示权限,则其权限可以访问
* hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
* hasRole | 如果有参数,参数表示角色,则其角色可以访问
* permitAll | 用户可以任意访问
* rememberMe | 允许通过remember-me登录的用户访问
* authenticated | 用户登录后可访问
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);
http.addFilterAfter(new MyPermitLoginFilter(), UsernamePasswordAuthenticationFilter.class);
//关闭csrf
http.csrf().disable()
// .sessionManagement()// 基于token,所以不需要session
// .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// .and()
//未登陆时返回 JSON 格式的数据给前端
.httpBasic().authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.authorizeRequests()
//任何人都能访问这个请求
.antMatchers("/captcha").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
//登录页面 不设限访问
.loginPage("/login.html")
//拦截的请求
.loginProcessingUrl("/login")
// 登录成功
.successHandler(authenticationSuccessHandler)
// 登录失败
.failureHandler(authenticationFailureHandler)
.permitAll()
.and()
.rememberMe().rememberMeParameter("rememberme")
// 防止iframe 造成跨域
.and()
.headers()
.frameOptions()
.disable()
.and();
// 禁用缓存
http.headers().cacheControl();
// 无权访问 JSON 格式的数据
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
}
成跨域
.and()
.headers()
.frameOptions()
.disable()
.and();
// 禁用缓存
http.headers().cacheControl();
// 无权访问 JSON 格式的数据
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
}
原文链接:https://blog.csdn.net/qq_37442469/article/details/117306474
作者:我长得真不赖
链接:http://www.javaheidong.com/blog/article/207412/20c9351608ee5b467a5d/
来源:java黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 java黑洞网 All Rights Reserved 版权所有,并保留所有权利。京ICP备18063182号-2
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!