SpringSecurity问题汇总

标签: Springboot 分类: 杂文 创建时间:2020-07-22 03:18:35 更新时间:2023-10-20 11:23:26

1.OPTIONS返回401错误

在配置了SpringSecurity之后,所有的OPTIONS请求都变成了401。即便我自定义了AccessDeniedHandler,AuthenticationEntryPoint也不走相应的代码。

1
2
3
4
5
6
7
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll() // 对option不校验
.anyRequest().authenticated();
}

在参考文章中,有两种放行策略:
(1) 一种是configure(WebSecurity web) 方法中配置放行。这种是不走过滤器链的。
(2) 一种是在 configure(HttpSecurity http) 方法中进行配置。这种是走过滤器链的。

2.跨域配置

1
2
3
4
5
6
7
8
9
@Override
public void configure(HttpSecurity http) throws Exception {
http.
//设置跨域, 如果不设置, 即使配置了filter, 也不会生效
.cors()
.and()
.headers().frameOptions().disable();
}

3.permitAll不起作用

当我在配置的时候,准备忽略掉某些请求的时候,使用了permitAll,本以为使用之后,就不再走过滤器链了,可是,当我定义了”/login”, “/index”,”/loginWX”,”/get_auth_access_token”,”/getUserInfoToken”,”/wechatBridge”,”/wxapi/getJsSDK”,这些路由,最后还是会出现在tokenAuthenticationFilter过滤器链中,认证不成功,也还是会返回授权失败的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 @Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.accessDeniedHandler(new SimpleAccessDeniedHandler())
.authenticationEntryPoint(new SimpleAuthenticationEntryPoint())
.and()
.authorizeRequests()
.antMatchers("/login", "/index","/loginWX",
"/get_auth_access_token","/getUserInfoToken","/wechatBridge","/wxapi/getJsSDK")
.permitAll()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.and()
.antMatcher("/**")
.addFilterAfter(tokenAuthenticationFilter, BasicAuthenticationFilter.class)
.authorizeRequests()
.anyRequest().hasRole("WX")
.and()
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// 配置跨域
.and()
.cors()
.and()
.headers().frameOptions().disable();;
}

第一种解决方法,使用WebSecurity进行路由忽略

1
2
3
4
5
6
@Override
public void configure(WebSecurity web) {
//解决静态资源被拦截的问题
web.ignoring().antMatchers("/login", "/index","/loginWX",
"/get_auth_access_token","/getUserInfoToken","/wechatBridge","/wxapi/getJsSDK","/checklogin");
}

这里要注意,如果你这样写了,那么就不能使用@Component注解自定义的过滤器,否则,上面的配置还是不起作用,最后所有的请求还是会走自定义的过滤器链。

解决web.ignoring()失效及TokenAuthenticationFilter中注入@Autowired注入失败的问题

那如果我既想忽略相关的路由,又想让filter中的自动注入生效怎么办呢?web应用启动的顺序顺是:listener->filter->servlet
可以使用构造函数注入法:在filter中新建一个带参数的构造函数,然后将RedisUtils作为参数传入。这里写不写@Autowired,好像都没有关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class TokenAuthenticationFilter extends OncePerRequestFilter{
private static final Logger logger = LoggerFactory.getLogger(TokenAuthenticationFilter.class);

private RedisUtils redisUtils;

public TokenAuthenticationFilter(){}

// @Autowired
public TokenAuthenticationFilter(RedisUtils redisUtils){
this.redisUtils=redisUtils;
}
}

通过构造函数的方式将需要的Bean注入到TokenAuthenticationFilter。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

// 自动注入bean
@Autowired
private RedisUtils redisUtils;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new TokenAuthenticationProvider());
}

@Override
public void configure(WebSecurity web) {
//解决静态资源被拦截的问题
web.ignoring().antMatchers("/login", "/index","/loginWX",
"/get_auth_access_token","/getUserInfoToken","/wechatBridge","/wxapi/getJsSDK","/checklogin");
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.accessDeniedHandler(new SimpleAccessDeniedHandler())
.authenticationEntryPoint(new SimpleAuthenticationEntryPoint())
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.and()
.antMatcher("/**")
// 注入redisUtils
.addFilterAfter(new TokenAuthenticationFilter(redisUtils), BasicAuthenticationFilter.class)
.authorizeRequests()
.anyRequest().hasRole("WX")
.and()
.csrf()
.disable()
// 配置session,注意下面是无状态的Session
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// 配置跨域
.and()
.cors()
.and()
.headers().frameOptions().disable();
}
}
参考文章:
1.Spring Security 自定义 AuthenticationProvider无法@Autowired问题 (在AuthenticationProvider中使用@Autowired始终为null,使用了return一个新的类的方式)
2.Springboot注入带参数的构造函数 ()
3.@Autowired的使用:推荐对构造函数进行注释 ()
4.Use @Autowired with a Filter configured in SpringBoot (这篇文章中也是用构造函数的方式,注入了SpringBoot的Bean)
5.filter过滤器使用Autowired注入Bean为null (使用ServletContext获取相应的Bean,这个我倒是没有尝试过)
6.SpringBoot Filter中注入Bean (使用构造函数的方式注入Bean)
7.Spring Boot配置接口 WebMvcConfigurer (如何配置WebMvcConfigurer)
8.如何在Java的Filter中注入Service??? (也讲了和三种方法在filter中注入Service)
9.8.6 Spring Boot集成Spring Security (从刚开始的默认自动生成用户名密码到最后的自定义userDetailsService,从数据库中取出数据进行验证,文章非常的长)

4.session管理

1
2
3
4
5
6
7
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// 配置session,注意下面是无状态的Session
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
}

5.URL contained a potentially malicious String “//“

双斜杠被返回错误,将spring security部署到服务器上,前端调用总是出现问题:org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String “//“

但是我的请求里面没有双斜杠啊,查看nginx的日志,发现还是一个斜杠啊

使用nginx进行配置

最后修改了nginx的配置,解决了相关的问题。还有就是通过增加过滤器的方式进行的强制通过,就像下面的两篇文章。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
## 设置跨域白名单
set $cors_origin "";
if ($http_origin ~* "^http://dev.proheng.net") {
set $cors_origin $http_origin;
}
if ($http_origin ~* "^http://localhost") {
set $cors_origin $http_origin;
}

## 设备扫码后台
location ^~/phdevice {
client_max_body_size 40m;

## 将这个配置,去掉了一个斜杠
##proxy_pass http://localhost:8080/$request_uri;

## 改为了下面的配置
proxy_pass http://localhost:8080$request_uri;


proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;

## 跨域

add_header Access-Control-Allow-Origin $cors_origin;
add_header Access-Control-Allow-Methods "POST, GET, OPTIONS";
add_header Access-Control-Allow-Headers "Origin, Authorization,X-Requested-With,Content-Type, Accept";
add_header Access-Control-Allow-Credentials true;

if ($request_method = 'OPTIONS') {
return 204;
}

}

使用代码解决

org.springframework.boot:spring-boot-starter-security:2.3.1.RELEASE,这是我的secuirty版本。

有效的一种方法就是直接定义个默认的Bean,定义了这个defaultHttpFirewall之后,不需要Configure中注入也可以使用。

1
2
3
4
@Bean
public HttpFirewall defaultHttpFirewall() {
return new DefaultHttpFirewall();
}

还有就是定义双斜杠忽略

1
2
3
4
5
6
7
8
@Bean
public HttpFirewall defaultHttpFirewall() {
// 解决双斜杠问题
StrictHttpFirewall firewall = new StrictHttpFirewall();
// 允许双斜杠
firewall.setAllowUrlEncodedDoubleSlash(true);
return firewall;
}
参考文章:
1.Spring security on the URL contains double slashes are blocked by permission processing 这里是通过设置了一个filter,在执行Security之前,先把url的双斜杠,改为单斜杠解决问题。
2.Spring Security 之 StrictHttpFirewall 坑 这篇文章我有点看不懂,虽然他也是设置无效,但是又说defaultHttpFirewall有效,我很是不能理解。
3.The request was rejected because the URL contained a potentially malicious String “;”报错解决 这个直接在Application中写了allowUrlSemicolonHttpFirewall这个Bean,但是实际上无效。
4.Spring Security 自带防火墙!你都不知道自己的系统有多安全! 我在这里才找到了为什么我配置双斜杠无效不起作用的问题。
5.spring-security使用-安全防护HttpFirewall(七) 提供了类图DefaultfHttpFirewall和StrictHttpFirewall都是继承自HttpFirewall,有这几种限制:限制请求方法、请求地址不能有分号、必须是可打印的 ASCII 字符、不能使用双斜杠、% 不被允许、反斜杠不被允许、. 不被允许
6.Spring Security Web : StrictHttpFirewall HTTP防火墙(严格模式) 包括了各种分析和源码展示

无效的解决方案

使用代码时,第一步创建一个HttpFirewall

1
2
3
4
5
6
@Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedSlash(true);
return firewall;
}

第二步,注入这个创建的Bean

1
2
3
4
5
6
7
@Override
public void configure(WebSecurity web) throws Exception {
//@formatter:off
super.configure(web);
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
....
}

经过上面的方法,我本以为可以了,但是实际上,还是会报错,我不知道自己那里做错了。
这个方法虽然说无效,其实是因为针对我的双斜杠无效,其实应该是setAllowUrlEncodedDoubleSlash(true)

参考文章:
1.Spring 5.0.3 RequestRejectedException: The request was rejected because the URL was not normalized (这里主要提到的是Security问题,只需要增加一个HttpFirewall的配置,允许双斜杠就可以了。)
2.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String “;” 这篇文章从发现问题,到一步步到debug,表明了如何去除“;“这个问题,虽然不是双斜杠问题,但是也差不多
3.Customized HttpFirewall to allow // is not applied at StrictHttpFirewall (创建了一个looseHttpFirewall的Bean,和我的方法差不多)
4.SpringSecurity手动忽略双斜杠校验//以及修改Jar包内源码 这里直接修改了源码,然后又倒入回去了,我觉得这样不妥。
小额赞助
本人提供免费与付费咨询服务,感谢您的支持!赞助请发邮件通知,方便公布您的善意!
**光 3.01 元
Sun 3.00 元
bibichuan 3.00 元
微信公众号
广告位
诚心邀请广大金主爸爸洽谈合作
每日一省
isNaN 和 Number.isNaN 函数的区别?

1.函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。

2.函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。

每日二省
为什么0.1+0.2 ! == 0.3,如何让其相等?

一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2-52,在ES6中,提供了Number.EPSILON属性,而它的值就是2-52,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3。

每日三省
== 操作符的强制类型转换规则?

1.首先会判断两者类型是否**相同,**相同的话就比较两者的大小。

2.类型不相同的话,就会进行类型转换。

3.会先判断是否在对比 null 和 undefined,是的话就会返回 true。

4.判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number。

5.判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断。

6.判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断。

每日英语
Happiness is time precipitation, smile is the lonely sad.
幸福是年华的沉淀,微笑是寂寞的悲伤。