Spring Security入门

2023/11/19 SpringSecuritySpring

# Spring Security入门

# 1、导入依赖

在springboot项目构造器中加入Spring security依赖

通过构造器菜单中的Security->Spring Security来选择

  • 在导入依赖后,web项目在访问资源的时候会自动生成一个登录页面
  • 如果不配置账号密码,则账号是user,密码会在控制台里生成
  • 配置账号密码如下
spring.security.user.name=sunday
spring.security.user.password=123456
1
2

# 2、SpringSecurity的基本组件,流程分析

  • Spring Security通过一系列的过滤器完成了用户身份认证及其授权工作,每个过滤器都有不同分工。
  • 然这些过滤器并不是全部都一起工作,而是根据我们需要什么功能,才会选取对应的过滤器加入。 当然这些过滤器并不是直接加入web的过滤器中,而是通过一个过滤器代理完成。
  • web过滤器中只会加入一个或多个过滤器代理,然后由这些代理负责管理哪些Security Filter需要加入进来。

# 2.1 组件介绍

# 2.1.1 Authentication(Principal)

封装用户身份信息,顶层接口,主要实现如下

  • AbstractAuthenticationToken()
    • RememberMeAuthenticationToken remember Me登陆后封装的身份信息
    • UsernamePasswordAuthenticationToken用户名密码登录后封装的身份信息

# 2.1.2 AuthenticationManager

身份认证器的代理,主要负责多个认证器的代理,管理多个AuthenticationProvider,主要实现如下

  • ProviderManager (authenticate)

# 2.1.3 AuthenticationProvider

  • 真正实现认证工作,多个provider受AuthenticationManager管理,主要实现如下

  • AbstractUserDetailsAuthenticationProvider

    • DaoAuthenticationProvide
    • RememberMeAuthenticationProvider

# 2.1.4 UserDetailsService

负责定义用户信息的来源,从不同来源加载用户信息,唯一的方法: loadUserByUsername,主要实 类:

  • UserDetailsManager

    • InMemoryUserDetailsManager
    • JdbcUserDetailsManager
  • 自定义

# 2.1.5 UserDetails

定义用户身份信息,比Authentication 信息更详细,主要实现

  • User

一般我们自定义

# 2.1.6 SecurityContextHolder

存放和获取用户身份信息的帮助类

# 2.1.6 FilterChainProxy

Spring Securty Filter的入口,FilterChainProxy管理多个filter

# 2.1.7 AbstractHttpConfigurer

构建所有过滤器的核心组件,主要方法init()configure(),

主要实现类

  • FormLoginConfigurer
  • CorsConfigurer
  • CsrfConfigurer
  • HttpBasicConfigurer
  • LogoutConfigurer ...

# 2.2 认证流程图

image-20231019094532569

# 2.3 Security配置类

@Configuration
public class SecurityConfig{
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(//开启权限配置
                auth->{
                    try{
                            auth.anyRequest().authenticated()//所有请求都要验证
                                    .and()//从头开始配置
                                    .formLogin()//开启表单登录,引入表单登录过滤器
                                    .loginPage("/login.html")//自定义登录页面
                                    .loginProcessingUrl("/login")//定义登录提交的接口地址

                                    /*
                                     *  设置登录失败跳转的地址,不携带参数,需要参数可以用
                                     *  .failureForwardUrl("controller层的地址")
                                     */
                                    .failureUrl("/login.html")
                                    /*
                                     *  设置成功时返回的json,
                                     *  如果想设置失败返回的Json可以用
                                     *  .failureHandler(),用法和下面一样
                                     */
                                    .successHandler(new AuthenticationSuccessHandler() {
                                        @Override
                                        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                                            HashMap<String, Object> map =new HashMap<>();
                                            map.put("code",0);
                                            map.put("msg","登录成功");
                                            map.put("data",authentication);
                                            response.setContentType("application/json;charset=utf-8");
                                            try{
                                                response.getWriter().println(JSON.toJSONString(map));
                                            }catch (Exception e){
                                                throw  new RuntimeException(e);
                                            }
                                        }
                                    })
                                    .defaultSuccessUrl("/hello")//设置成功后跳转的地址,要写在successHandler下面
                                    .permitAll()//允许这些地址不需要认证
                                    .and()//从头开始配置
                                    .csrf().disable();//security支持csrf防护.,默认开启,这里需要关闭
                    }catch (Exception e){
                        throw new RuntimeException(e);
                    }
                }
        );
        return http.build();
    }
}
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
50

# 3、获取登录信息

# 1、SecurityContextHolder

    @RequestMapping("/")//不配置defaultSuccessUrl和successHandler默认登录成功到根目录
    public String loginInfo(){
        Authentication authentication= SecurityContextHolder.getContext().getAuthentication();
        return JSON.toJSONString(authentication);
    }
1
2
3
4
5

# 2 、参数里自动注入Authentication

@RequestMapping("/auth")
    public String auth(Authentication authentication){
        return JSON.toJSONString(authentication);
    }
1
2
3
4

# 3、异步线程情况下获取登录信息

在异步线程中拿不到登录信息,这个其实可以通过调整他的策略来实现在异步线程中获取用 户信息。

# SecurityContextHolder 主要有三种策略:

# MODE THREADLOCAL

默认策略,使用THREADLOCAL实现,只在当前线程中保存用户信息

# MODEINHERITABLETHREADLOCAL

支持在多线程中传递用户信息,使用这种策略,当我们启动新线程时,security会将当前线程的用户信 息拷贝一份到新线程中

用法:在虚拟机参数里加上-Dspring.security,strategy=MODE_INHERITABLETHREADLOCAL

# MODE GLOBAL

security将数据保存在一个全局变量中,也能解决多线程问题,一般很少用 如果使用默认策略,在异步线程中获取用户信息,返回为空:

# 4、基于多种方式配置登录用户:memory、jdbc,mybatis

# 基于内存

@Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager(){
    UserDetails userDetails1 = User.withUsername("memory1").password("{noop}memory1").roles("roles1").build();
    UserDetails userDetails2 = User.withUsername("memory2").password("{noop}memory2").roles("roles2").build();
    return new InMemoryUserDetailsManager(userDetails1,userDetails2);
}
1
2
3
4
5
6

{noop}指的是不加密的形式传递密码

# 基于mybatis

最后更新于: 2024/2/27 17:14:39