SpringSecurity入门

标签: Springboot 分类: Java 创建时间:2020-07-05 07:45:22 更新时间:2023-10-20 11:23:26

关于Shiro和SpringSecurity我一直想不明白到底该在项目中使用谁。其实本质上还是没有优秀的项目经验或者说是没有看过真正的优秀的项目源码。

1.添加依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
参考文章:
1.Spring Security 自定义登录认证(二) (刚开始参考的文章,这篇文章对securityconfig的配置注释说的很详细,这里有一个ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry,好像和别的文章不一样)

2.ajax登录验证的方式

像下面的几篇文章,都是进行了UserDetailsService的方式进行的。经过的我的分析和判断,其实为什么会有addFilterAt这个方法,即便你不想用UsernamePasswordAuthenticationFilter,也还是需要写,这其实是一个SpringSecurity的过滤器链的执行过成,也就是将自定义的过滤器插入到UsernamePasswordAuthenticationFilter之前。

参考文章:
1.SpringBoot+SpringSecurity处理Ajax登录请求
2.Spring Boot+Spring Security+Ajax 实现自定义登录
4.spring security ajax登录及返回
5.Spring Security-結合RestfulAPI的設計 (这篇文章是用繁体写的,不过就如何使用SpringSecurity进行Restful风格的Api还是很有帮助的)
6.一、Srping security 4 filter 别名及顺序 (这篇讲了过滤器及其执行顺序的问题,对每个过滤器进行了说明和讲解。)

3.微信公众号授权模式

在不断的深入的学习中,我发现了一个叫Spring Social的东西。有不少的微信登录的文章,都是使用了Spring Social这一个工具。通过Spring Social 官网,我发现了非常多的的项目,比如Spring Socail Weibo,领英,github等。

参考文章:
1.从零开始的Spring Security Oauth2(一)
2.springboot spring-security 集成微信登录
3.spring-security-oauth2(十三 ) 微信登录 (这篇文章中代码很多,但是没有说明,整个认证的逻辑,以及和Spring Security的结合,还是需要自己去参悟的。)
4.Spring Security源码分析四:Spring Social实现微信社交登录 (这个项目感觉很了不起的样子,是一个系列文章,包括了很多东西。如何使用Social实现QQ、微信和Github等登录)
5.Spring Social (这是官网平台)

4.手机验证码登录

认证的流程:

1
2
3
4
5
6
7
8
9
10
生成未认证的AuthenticationToken                 
↑(获取信息) (根据AuthenticationToken分配provider)
AuthenticationFilter -> AuthenticationManager -> AuthenticationProvider
↓(认证)
UserDetails(一般查询数据库获取)
↓(通过)
生成认证成功的AuthenticationToken
↓(存放)
SecurityContextHolder

参考文章:
1.spring security 自定义认证登录 (自定义手机验证码登录流程,这个我觉得挺实用的,可以改造成微信公众号授权登录的逻辑。这里还有一个就是在过滤器中使用了@Component,在过滤器中注入了@Autowired,但是我用他的方法,最后还是注入的null。最后我知道了,主要是要在安全配置类SecurityConfig中,使用@Autowired注入tokenAuthenticationFilter,代替new TokenAuthenticationFilter())
2.Spring Security 实现 API Token 权限认证 (这里提供了一个思路,就是如何实现API Token的认证登录)
3.SpringSecurity+JWT认证流程解析 | 掘金新人第一弹 (讲了如何使用JWT进行token认证)

5.自定义API token鉴权

根据多天的尝试,鉴于参考文章Spring Security 实现 API Token 权限认证,我写了一个简单的查找url中有没有token字段的权限认证流程,类似于:http://localhost/QRcode/rsa?token=110,如果有token字段,则通过,如果没有token字段,则禁止通过。

1.引入依赖

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>

2.自定义Authentication

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
public class TokenAuthentication implements Authentication {
private String token;

public TokenAuthentication(String token) {
this.token = token;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}

@Override
public Object getCredentials() {
return token;
}

@Override
public Object getDetails() {
return null;
}

@Override
public Object getPrincipal() {
return null;
}

@Override
public boolean isAuthenticated() {
return false;
}

@Override
public void setAuthenticated(boolean authenticated) throws IllegalArgumentException {
}

@Override
public String getName() {
return "token";
}
}

2.自定义Filter

在Filter中定义何时才进入认证的流程,即TokenAuthentication。这里主要是为了创建一个TokenAuthentication,这个在创建的时候,刚开始isAuthenticated的状态是false,只有等到调用setAuthenticated方法的设置成true,这个时候就标明了用户已经认证过了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class TokenAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain fc)
throws ServletException, IOException {

SecurityContext context = SecurityContextHolder.getContext();
if (context.getAuthentication() != null && context.getAuthentication().isAuthenticated()) {
// do nothing
} else {
// ②
Map<String, String[]> params = req.getParameterMap();
if (!params.isEmpty() && params.containsKey("token")) {
String token = params.get("token")[0];
if (token != null) {
Authentication auth = new TokenAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
req.setAttribute("com.proheng.gis.security.TokenAuthenticationFilter.FILTERED", true); //③
}

fc.doFilter(req, res); //④
}
}

4.自定义AuthenticationProvider

在Provider中定义,当过滤器拦截了请求,并达到了认证条件时,如何进行基本的权限认证。这里,我生成了一个假的用户和角色。

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
public class TokenAuthenticationProvider implements AuthenticationProvider {

@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {

if (authentication.isAuthenticated()) {
return authentication;
}

String token = authentication.getCredentials().toString();

if (Strings.isNullOrEmpty(token)) {
return authentication;
}
// 生成用户信息
UserDetails user = User.builder()
.username("api")
.password("")
.authorities(new SimpleGrantedAuthority("ROLE_API"))
.build();

Authentication auth = new PreAuthenticatedAuthenticationToken(
user, token,user.getAuthorities());

// 这一步很关键,就是设置了通过认证的条件
auth.setAuthenticated(true);
return auth;
}

@Override
public boolean supports(Class<?> aClass) {
return (TokenAuthentication.class.isAssignableFrom(aClass));
}
}

5.自定义WebSecurityConfigurerAdapter

在configure(AuthenticationManagerBuilder auth)中注入了认证的流程,在configure(HttpSecurity http)中注入了过滤器。

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
/**
* 一个安全配置类,可以实现多个安全配置类,
* 每一个安全配置类都包含了一条过滤器链
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new TokenAuthenticationProvider());
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/QRcode/**")
.addFilterAfter(new TokenAuthenticationFilter(), BasicAuthenticationFilter.class)
.authorizeRequests()
.anyRequest().hasRole("API")
.and()
.csrf()
.disable()
}
}

上面的示例中,只包括了简单的认证逻辑,也没有设及到数据库及角色等信息,只能作为简单的流程理解罢了,其实还可以继续进行精简。

如果想在TokenAuthenticationFilter中能使用@Autowired自动注入,就要将new TokenAuthenticationFilter()换成@Autowired注入的形式,比如:

1
2
3
4
5
@Autowired
private TokenAuthenticationFilter tokenAuthenticationFilter;

http....
.addFilterAfter(tokenAuthenticationFilter, BasicAuthenticationFilter.class)
参考文章:
1.Spring security (一)架构框架-Component、Service、Filter分析
2.Use @Autowired with a Filter configured in SpringBoot (这篇文章中也是用构造函数的方式,注入了SpringBoot的Bean)
3.filter过滤器使用Autowired注入Bean为null (使用ServletContext获取相应的Bean)
4.SpringBoot Filter中注入Bean (使用构造函数的方式注入Bean)

6.自定义异常处理

对于认证失败和授权失败的情况下,都要自定义错误处理。下面的自定义异常处理,可以借鉴了。主要是实现AuthenticationEntryPoint接口,进行认证异常的处理,以及AccessDeniedHandler接口,实现授权失败的场景。然后再config中注入http.exceptionHandling().accessDeniedHandler(new SimpleAccessDeniedHandler()).authenticationEntryPoint(new SimpleAuthenticationEntryPoint());

小额赞助
本人提供免费与付费咨询服务,感谢您的支持!赞助请发邮件通知,方便公布您的善意!
**光 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.
幸福是年华的沉淀,微笑是寂寞的悲伤。