SpringSecurity入门(一)

1、引入依赖

spring-boot版本2.7.3,如未特殊说明版本默认使用此版本

xml 复制代码
      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
  			<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>

2、编写controller并启动springboot服务

java 复制代码
@RestController
public class HelloController {

    @GetMapping("/")
    public String hello(){
        return "hello SpringSecurity";
    }
}
  • 启动
  • 登陆使用账号:user,密码:04e74f23-0e97-4ee9-957e-2004a2e60692
  • SecurityProperties

3、自动配置SpringBootWebSecurityConfiguration

  • SecurityFilterChainConfiguration
  • WebSecurityConfigurerAdapter中有所有的Security相关的配置,只需要继承重新对应属性即可完成自定义
  • 由于新版本的Security已经弃用WebSecurityConfigurerAdapter所以注册SecurityFilterChain即可
java 复制代码
  @Bean
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeRequests()
                .mvcMatchers("/index").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and().build();
    }

4、默认登陆页面DefaultLoginPageGeneratingFilter

4.1、SecurityFilterChainConfiguration默认实现的SecurityFilterChain

  • 容器中没有WebSecurityConfigurerAdapter类型的bean实例自动配置才会生效

4.2、UsernamePasswordAuthenticationFilter

4.3、attemptAuthentication方法

4.4、 ProviderManager的authenticate方法

4.5、 AuthenticationProvider实现AbstractUserDetailsAuthenticationProvider中的authenticate方法

4.6、 UserDetails实现类DaoAuthenticationProvider的retrieveUser方法

4.7、UserDetailsService实现类InMemoryUserDetailsManager的loadUserByUsername方法

4.8、 UserDetailsService

4.9、 UserDetailsServiceAutoConfiguration

  • 容器中没有:AuthenticationManager、AuthenticationProvider、UserDetailsService、AuthenticationManagerResolver这4个bean实例才会加载InMemoryUserDetailsManager


4.10、 SecurityProperties



  • 可以通过spring.security.user.password=123456自定义密码

5、 自定义认证

5.1、由于新版本的Security已经弃用WebSecurityConfigurerAdapter所以注册SecurityFilterChain即可

github示例

java 复制代码
    @Bean
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeRequests()
                .mvcMatchers("/index").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and().build();
    }

5.2、 自定义登陆页面

5.2.1、html

  • 使用UsernamePasswordAuthenticationFilter用户名和密码字段名必须是username和password,且必须是POST的方式提交
html 复制代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
<form th:action="@{/doLogin}" method="post">
    <p>用户名:<label>
        <input name="username" type="text"/>
    </label></p>
    <p>密码:<label>
        <input name="password" type="password"/>
    </label></p>
    <p>
        <input type="submit">
    </p>
</form>

</body>
</html>

5.2.2、SecurityFilterChain配置

java 复制代码
@Configuration
public class WebSecurityConfigurer {

    @Bean
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return
                //开启权限验证
                httpSecurity.authorizeRequests()
                //permitAll直接放行,必须在anyRequest().authenticated()前面
                .mvcMatchers("/toLogin").permitAll()
                .mvcMatchers("/index").permitAll()
                //anyRequest所有请求都需要认证
                .anyRequest().authenticated()
                .and()
                //使用form表单验证
                .formLogin()
                //自定义登陆页面
                .loginPage("/toLogin")
                //自定义登陆页面后必须指定处理登陆请求的url
                .loginProcessingUrl("/doLogin")
                .and()
                 //禁止csrf跨站请求保护
                .csrf().disable()
                .build();
    }

5.2.3、 controller

java 复制代码
@Controller
public class LoginController {

    @RequestMapping("toLogin")
    public String toLogin(){
        return "login";
    }
}

5.2.4、 自定义登陆使用的用户名和密码字段名使用usernameParameter和passwordParameter

java 复制代码
@Configuration
public class WebSecurityConfigurer {

    @Bean
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return
                //开启权限验证
                httpSecurity.authorizeRequests()
                //permitAll直接放行,必须在anyRequest().authenticated()前面
                .mvcMatchers("/toLogin").permitAll()
                .mvcMatchers("/index").permitAll()
                //anyRequest所有请求都需要认证
                .anyRequest().authenticated()
                .and()
                //使用form表单验证
                .formLogin()
                //自定义登陆页面
                .loginPage("/toLogin")
                //自定义登陆页面后必须指定处理登陆请求的url
                .loginProcessingUrl("/doLogin")
//               自定义接收用户名的参数名为uname
                .usernameParameter("uname")
//               自定义接收密码的参数名为pwd
                .passwordParameter("pwd")
                .and()
                 //禁止csrf跨站请求保护
                .csrf().disable()
                .build();
    }
}
  • form表单中对应参数名也需要修改,用户名为:uname,密码为:pwd

5.3、 自定义认证成功后访问的页面

  • successForwardUrl(转发),必须使用POST请求,每次都会跳转到指定请求
  • defaultSuccessUrl(重定向),必须使用GET请求,不会每次都跳转定义的页面,默认会记录认证拦截的请求,如果是拦截的受限资源会优先跳转到之前被拦截的请求。需要每次都跳转使用.defaultSuccessUrl("/test",true)即可
  • 二选一
java 复制代码
//               登陆认证成功后跳转的页面(转发),必须使用POST请求
//                .successForwardUrl("/test")
//               陆认证成功后跳转的页面(重定向),必须使用GET请求
                 .defaultSuccessUrl("/test",true)

5.4、 前后端分离处理方式

5.4.1、 实现AuthenticationSuccessHandler接口

java 复制代码
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        Map<String,Object> map = new HashMap<>();
        map.put("msg", "登陆成功");
        map.put("code", HttpStatus.OK);
        map.put("authentication", authentication);
        String s = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(s);
    }
}

5.4.2、修改SecurityFilterChain配置

  • 使用successHandler(new MyAuthenticationSuccessHandler())
java 复制代码
@Configuration
public class WebSecurityConfigurer {

    @Bean
    @SuppressWarnings("all")
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return
                //开启权限验证
                httpSecurity.authorizeRequests()
                //permitAll直接放行,必须在anyRequest().authenticated()前面
                .mvcMatchers("/toLogin").permitAll()
                .mvcMatchers("/index").permitAll()
                //anyRequest所有请求都需要认证
                .anyRequest().authenticated()
                .and()
                //使用form表单验证
                .formLogin()
                //自定义登陆页面
                .loginPage("/toLogin")
                //自定义登陆页面后必须指定处理登陆请求的url
                .loginProcessingUrl("/doLogin")
//               自定义接收用户名的参数名为uname
                .usernameParameter("uname")
//               自定义接收密码的参数名为pwd
                .passwordParameter("pwd")
//               登陆认证成功后跳转的页面(转发),必须使用POST请求
//                .successForwardUrl("/test")
//               陆认证成功后跳转的页面(转发),必须使用GET请求
//                 .defaultSuccessUrl("/test",true)
                 //不会每次都跳转定义的页面,默认会记录认证拦截的请求,如果是拦截的受限资源会优先跳转到之前被拦截的请求。需要每次都跳转使defaultSuccessUrl("/test",true)
//                 .defaultSuccessUrl("/test")
//                前后端分离时代自定义认证成功处理
                .successHandler(new MyAuthenticationSuccessHandler())
                .and()
                 //禁止csrf跨站请求保护
                .csrf().disable()
                .build();
    }
}

5.4.3、返回数据

6、 认证失败处理

  • failureForwardUrl,转发,请求必须是POST
  • failureUrl,重定向,请求必须是GET

6.1、org.springframework.security.authentication.ProviderManager#authenticate

6.2、 org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter#doFilter(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain)

6.3、 org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter#unsuccessfulAuthentication

6.4、 org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler#onAuthenticationFailure

6.5、 org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler#saveException

  • 如果是转发异常信息存在request里面
  • 如果是重定向异常信息存在session里面,默认是重定向
  • 参数名:SPRING_SECURITY_LAST_EXCEPTION

6.7、 前端取值展示

  • 修改SecurityFilterChain配置
java 复制代码
@Configuration
public class WebSecurityConfigurer {

    @Bean
    @SuppressWarnings("all")
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return
                //开启权限验证
                httpSecurity.authorizeRequests()
                //permitAll直接放行,必须在anyRequest().authenticated()前面
                .mvcMatchers("/toLogin").permitAll()
                .mvcMatchers("/index").permitAll()
                //anyRequest所有请求都需要认证
                .anyRequest().authenticated()
                .and()
                //使用form表单验证
                .formLogin()
                //自定义登陆页面
                .loginPage("/toLogin")
                //自定义登陆页面后必须指定处理登陆请求的url
                .loginProcessingUrl("/doLogin")
//               自定义接收用户名的参数名为uname
                .usernameParameter("uname")
//               自定义接收密码的参数名为pwd
                .passwordParameter("pwd")
//               登陆认证成功后跳转的页面(转发),必须使用POST请求
//                .successForwardUrl("/test")
//               陆认证成功后跳转的页面(转发),必须使用GET请求
//                 .defaultSuccessUrl("/test",true)
                 //不会每次都跳转定义的页面,默认会记录认证拦截的请求,如果是拦截的受限资源会优先跳转到之前被拦截的请求。需要每次都跳转使defaultSuccessUrl("/test",true)
//                 .defaultSuccessUrl("/test")
//                前后端分离时代自定义认证成功处理
                .successHandler(new MyAuthenticationSuccessHandler())
//               认证失败跳转页面,必须使用POST请求         
                .failureForwardUrl("/toLogin")
            //  认证失败跳转页面,,必须使用GET请求
                // .failureUrl("/toLogin")
                .and()
                 //禁止csrf跨站请求保护
                .csrf().disable()
                .build();
    }
}
  • html增加取值
html 复制代码
<!--  重定向错误信息存在session中  -->
<p th:text="${session.SPRING_SECURITY_LAST_EXCEPTION}"></p>
<!--  转发错误信息存在request中  -->
<p th:text="${SPRING_SECURITY_LAST_EXCEPTION}"></p>

6.8、 前后端分离处理方式

6.8.1、 实现AuthenticationFailureHandler接口

java 复制代码
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        Map<String,Object> map = new HashMap<>();
        map.put("msg", exception.getMessage());
        map.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
        String s = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(s);
    }
}

6.8.2、修改SecurityFilterChain配置

  • failureHandler
java 复制代码
@Configuration
public class WebSecurityConfigurer {

    @Bean
    @SuppressWarnings("all")
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return
                //开启权限验证
                httpSecurity.authorizeRequests()
                //permitAll直接放行,必须在anyRequest().authenticated()前面
                .mvcMatchers("/toLogin").permitAll()
                .mvcMatchers("/index").permitAll()
                //anyRequest所有请求都需要认证
                .anyRequest().authenticated()
                .and()
                //使用form表单验证
                .formLogin()
                //自定义登陆页面
                .loginPage("/toLogin")
                //自定义登陆页面后必须指定处理登陆请求的url
                .loginProcessingUrl("/doLogin")
//               自定义接收用户名的参数名为uname
                .usernameParameter("uname")
//               自定义接收密码的参数名为pwd
                .passwordParameter("pwd")
//               登陆认证成功后跳转的页面(转发),必须使用POST请求
//                .successForwardUrl("/test")
//               陆认证成功后跳转的页面(转发),必须使用GET请求
//                 .defaultSuccessUrl("/test",true)
                 //不会每次都跳转定义的页面,默认会记录认证拦截的请求,如果是拦截的受限资源会优先跳转到之前被拦截的请求。需要每次都跳转使defaultSuccessUrl("/test",true)
//                 .defaultSuccessUrl("/test")
//                前后端分离时代自定义认证成功处理
                .successHandler(new MyAuthenticationSuccessHandler())
//               认证失败跳转页面,必须使用POST请求
//                .failureForwardUrl("/toLogin")
//               认证失败跳转页面,必须使用GET请求
//                 .failureUrl("/toLogin")
//               前后端分离时代自定义认证失败处理
                .failureHandler(new MyAuthenticationFailureHandler())
                .and()
                 //禁止csrf跨站请求保护
                .csrf().disable()
                .build();
    }
}

7、 注销登录

7.1、 默认方式

.logout()

// 指定注销url,默认请求方式GET

.logoutUrl("/logout")

// 注销成功后跳转页面

.logoutSuccessUrl("/toLogin")

java 复制代码
@Configuration
public class WebSecurityConfigurer {

    @Bean
    @SuppressWarnings("all")
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return
                //开启权限验证
                httpSecurity.authorizeRequests()
                //permitAll直接放行,必须在anyRequest().authenticated()前面
                .mvcMatchers("/toLogin").permitAll()
                .mvcMatchers("/index").permitAll()
                //anyRequest所有请求都需要认证
                .anyRequest().authenticated()
                .and()
                //使用form表单验证
                .formLogin()
                //自定义登陆页面
                .loginPage("/toLogin")
                //自定义登陆页面后必须指定处理登陆请求的url
                .loginProcessingUrl("/doLogin")
//               自定义接收用户名的参数名为uname
                .usernameParameter("uname")
//               自定义接收密码的参数名为pwd
                .passwordParameter("pwd")
//               登陆认证成功后跳转的页面(转发),必须使用POST请求
//                .successForwardUrl("/test")
//               陆认证成功后跳转的页面(转发),必须使用GET请求
//                 .defaultSuccessUrl("/test",true)
                 //不会每次都跳转定义的页面,默认会记录认证拦截的请求,如果是拦截的受限资源会优先跳转到之前被拦截的请求。需要每次都跳转使defaultSuccessUrl("/test",true)
//                 .defaultSuccessUrl("/test")
//                前后端分离时代自定义认证成功处理
                .successHandler(new MyAuthenticationSuccessHandler())
//               认证失败跳转页面,必须使用POST请求
//                .failureForwardUrl("/toLogin")
//               认证失败跳转页面,必须使用GET请求
//                 .failureUrl("/toLogin")
//               前后端分离时代自定义认证失败处理
                .failureHandler(new MyAuthenticationFailureHandler())
                .and()
//               注销
                .logout()
//               指定注销url,默认请求方式GET
                .logoutUrl("/logout")
//               销毁session,默认为true
                .invalidateHttpSession(true)
//               清除认证信息,默认为true
                .clearAuthentication(true)
//               注销成功后跳转页面
                .logoutSuccessUrl("/toLogin")
                .and()
                 //禁止csrf跨站请求保护
                .csrf().disable()
                .build();
    }
}

7.2、 自定义方式

// 注销

.logout()

// 自定义注销url

.logoutRequestMatcher(newOrRequestMatcher(

newAntPathRequestMatcher("/aa","GET"),

newAntPathRequestMatcher("/bb","POST")

))

// 注销成功后跳转页面

.logoutSuccessUrl("/toLogin")

java 复制代码
@Configuration
public class WebSecurityConfigurer {

    @Bean
    @SuppressWarnings("all")
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return
                //开启权限验证
                httpSecurity.authorizeRequests()
                //permitAll直接放行,必须在anyRequest().authenticated()前面
                .mvcMatchers("/toLogin").permitAll()
                .mvcMatchers("/index").permitAll()
                //anyRequest所有请求都需要认证
                .anyRequest().authenticated()
                .and()
                //使用form表单验证
                .formLogin()
                //自定义登陆页面
                .loginPage("/toLogin")
                //自定义登陆页面后必须指定处理登陆请求的url
                .loginProcessingUrl("/doLogin")
//               自定义接收用户名的参数名为uname
                .usernameParameter("uname")
//               自定义接收密码的参数名为pwd
                .passwordParameter("pwd")
//               登陆认证成功后跳转的页面(转发),必须使用POST请求
//                .successForwardUrl("/test")
//               陆认证成功后跳转的页面(转发),必须使用GET请求
//                 .defaultSuccessUrl("/test",true)
                 //不会每次都跳转定义的页面,默认会记录认证拦截的请求,如果是拦截的受限资源会优先跳转到之前被拦截的请求。需要每次都跳转使defaultSuccessUrl("/test",true)
//                 .defaultSuccessUrl("/test")
//                前后端分离时代自定义认证成功处理
                .successHandler(new MyAuthenticationSuccessHandler())
//               认证失败跳转页面,必须使用POST请求
//                .failureForwardUrl("/toLogin")
//               认证失败跳转页面,必须使用GET请求
//                 .failureUrl("/toLogin")
//               前后端分离时代自定义认证失败处理
                .failureHandler(new MyAuthenticationFailureHandler())
                .and()
//               注销
                .logout()
//               指定注销url,默认请求方式GET
//                .logoutUrl("/logout")
                .logoutRequestMatcher(new OrRequestMatcher(
                        new AntPathRequestMatcher("/aa","GET"),
                        new AntPathRequestMatcher("/bb","POST")
                ))
//               销毁session,默认为true
                .invalidateHttpSession(true)
//               清除认证信息,默认为true
                .clearAuthentication(true)
//               注销成功后跳转页面
                .logoutSuccessUrl("/toLogin")
                .and()
                 //禁止csrf跨站请求保护
                .csrf().disable()
                .build();
    }
}

7.3、 前后端分离

7.3.1、 实现LogoutSuccessHandler接口

java 复制代码
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        Map<String,Object> map = new HashMap<>();
        map.put("msg", "注销成功");
        map.put("code", HttpStatus.OK.value());
        map.put("authentication", authentication);
        String s = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(s);
    }
}

7.3.2、 修改SecurityFilterChain配置

  • logoutSuccessHandler
java 复制代码
@Configuration
public class WebSecurityConfigurer {

    @Bean
    @SuppressWarnings("all")
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return
                //开启权限验证
                httpSecurity.authorizeRequests()
                //permitAll直接放行,必须在anyRequest().authenticated()前面
                .mvcMatchers("/toLogin").permitAll()
                .mvcMatchers("/index").permitAll()
                //anyRequest所有请求都需要认证
                .anyRequest().authenticated()
                .and()
                //使用form表单验证
                .formLogin()
                //自定义登陆页面
                .loginPage("/toLogin")
                //自定义登陆页面后必须指定处理登陆请求的url
                .loginProcessingUrl("/doLogin")
//               自定义接收用户名的参数名为uname
                .usernameParameter("uname")
//               自定义接收密码的参数名为pwd
                .passwordParameter("pwd")
//               登陆认证成功后跳转的页面(转发),必须使用POST请求
//                .successForwardUrl("/test")
//               陆认证成功后跳转的页面(转发),必须使用GET请求
//                 .defaultSuccessUrl("/test",true)
                 //不会每次都跳转定义的页面,默认会记录认证拦截的请求,如果是拦截的受限资源会优先跳转到之前被拦截的请求。需要每次都跳转使defaultSuccessUrl("/test",true)
//                 .defaultSuccessUrl("/test")
//                前后端分离时代自定义认证成功处理
                .successHandler(new MyAuthenticationSuccessHandler())
//               认证失败跳转页面,必须使用POST请求
//                .failureForwardUrl("/toLogin")
//               认证失败跳转页面,必须使用GET请求
//                 .failureUrl("/toLogin")
//               前后端分离时代自定义认证失败处理
                .failureHandler(new MyAuthenticationFailureHandler())
                .and()
//               注销
                .logout()
//               指定默认注销url,默认请求方式GET
//                .logoutUrl("/logout")
//               自定义注销url
                .logoutRequestMatcher(new OrRequestMatcher(
                        new AntPathRequestMatcher("/aa","GET"),
                        new AntPathRequestMatcher("/bb","POST")
                ))
//               销毁session,默认为true
                .invalidateHttpSession(true)
//               清除认证信息,默认为true
                .clearAuthentication(true)
//               注销成功后跳转页面
//                .logoutSuccessUrl("/toLogin")
                .logoutSuccessHandler(new MyLogoutSuccessHandler())
                .and()
                 //禁止csrf跨站请求保护
                .csrf().disable()
                .build();
    }
}

相关文章

SpringSecurity入门(二)
SpringSecurity入门(三)
SpringSecurity入门(四)

未完待续

相关推荐
菜鸡且互啄691 小时前
在线教育平台,easyexcel使用案例
java·开发语言
八月林城1 小时前
JAVA导出数据库字典到Excel
java·数据库·excel
浅念同学3 小时前
算法-常见数据结构设计
java·数据结构·算法
杰哥在此5 小时前
Java面试题:讨论持续集成/持续部署的重要性,并描述如何在项目中实施CI/CD流程
java·开发语言·python·面试·编程
咖啡煮码5 小时前
深入剖析Tomcat(十五、十六) 关闭钩子,保证Tomcat的正常关闭
java·tomcat
C.C5 小时前
java IO流(1)
java·开发语言
刘铸纬7 小时前
Golang中defer和return顺序
开发语言·后端·golang
黑头!7 小时前
Tomcat注册为服务之后 运行时提示JVM异常
java·jvm·tomcat
袁震7 小时前
Java---Mybatis详解二
java·开发语言·mybatis
《黑巧克力》7 小时前
【JavaEE】多线程进阶
java·spring·java-ee·maven·dubbo·idea