spring boot(学习笔记第十二课)

spring boot(学习笔记第十二课)

  • Spring Security内存认证,自定义认证表单

学习内容:

  1. Spring Security内存认证
  2. 自定义认证表单

1. Spring Security内存认证

  1. 首先开始最简单的模式,内存认证。
    • 加入spring security的依赖。

      xml 复制代码
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-security</artifactId>
      </dependency>
    • 加入controller进行测试。

      java 复制代码
       @GetMapping("/security_hello")
       @ResponseBody
          public String hello(){
              return "hello,security";
          }
    • 启动应用程序。
      默认的用户名是user,密码会在log中出现。

      log 复制代码
      Using generated security password: 9b7cd16e-af9e-4804-a6a2-9303df66ace8
    • 访问controller,可以看到这里 * 输入上面的密码,进行login。

  2. 接着开始在内存中定义认证的用户和密码。
    • 定义内存用户,设定安全设置@configuration

      java 复制代码
      @Configuration
      public class SecurityConfig {
          @Bean
          PasswordEncoder passwordEncoder() {
              return NoOpPasswordEncoder.getInstance();
          }
      
          @Bean
          UserDetailsService userDetailsService() {
              InMemoryUserDetailsManager users =
                      new InMemoryUserDetailsManager();
              users.createUser(User.withUsername("finlay_admin")
                      .password("123456")
                      .roles("ADMIN")
                      .build());
              users.createUser(User.withUsername("finlay_dba")
                      .password("123456")
                      .roles("DBA")
                      .build());
              users.createUser(User.withUsername("finlay_super")
                      .password("123456")
                      .roles("ADMIN", "DBA")
                      .build());
              return users;
          }
      
          @Bean
          SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
              http.authorizeHttpRequests(auth -> auth.requestMatchers
                                      ("/**")//匹配所有/** url
                              .hasRole("ADMIN")//定义/**访问所需要ADMIN的role
                              .anyRequest()//设定任何访问都需要认证
                              .authenticated()//设定任何访问都需要认证
                      )
                      .csrf(csrf -> csrf.disable())//csrf跨域访问无效 Cross-Site Request Forgery
                      .sessionManagement(session -> session.maximumSessions(1).maxSessionsPreventsLogin(true));
              return http.build();
          }
    • finlay_dba这个user只设定了DBArole,login是无效的

    • finlay_super这个user只设定了DBArole,login是无效的

  3. 进一步 测试详细的权限设定。
    • 定义controller

      java 复制代码
       @GetMapping("/admin/hello")
          @ResponseBody
          public String helloAdmin() {
              return "hello,admin";
          }
      
          @GetMapping("/user/hello")
          @ResponseBody
          public String helloUser() {
              return "hello,user";
          }
      
          @GetMapping("/db/hello")
          @ResponseBody
          public String helloDB() {
              return "hello,DBA";
          }
      
          @GetMapping("/hello")
          @ResponseBody
          public String hello() {
              return "hello";
          }
    • 细化各种url的访问权限。

      java 复制代码
      @Configuration
      public class SecurityConfig {
          @Bean
          PasswordEncoder passwordEncoder() {
              return NoOpPasswordEncoder.getInstance();
          }
      
          @Bean
          UserDetailsService userDetailsService() {
              InMemoryUserDetailsManager users =
                      new InMemoryUserDetailsManager();
              users.createUser(User.withUsername("finlay_user")
                      .password("123456")
                      .roles("USER")
                      .build());
              users.createUser(User.withUsername("finlay_admin")
                      .password("123456")
                      .roles("ADMIN")
                      .build());
              users.createUser(User.withUsername("finlay_dba")
                      .password("123456")
                      .roles("DBA")
                      .build());
              users.createUser(User.withUsername("finlay_super")
                      .password("123456")
                      .roles("ADMIN", "DBA")
                      .build());
              return users;
          }
      
          @Bean
          SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
              httpSecurity.authorizeHttpRequests(
                              auth ->
                                      auth.requestMatchers("/admin/**")//匹配所有/** url
                                              .hasRole("ADMIN")//只能对于admin的role,才能访问
                                              .requestMatchers("/user/**")//匹配/user/**
                                              .hasRole("USER")//只有对于user的role,才能访问
                                              .requestMatchers("/db/**")//配置/db/**
                                              .hasRole("DBA")//只有对于dba的role,才能访问
                                              .anyRequest()
                                              .authenticated()//设定任何访问都需要认证
                      )
                      .formLogin(form -> form.loginProcessingUrl("/login")//这里对于前后端分离,提供的非页面访问url
                              .usernameParameter("username")//页面上form的用户名
                              .passwordParameter("password"))//页面上form的密码
                      .csrf(csrf -> csrf.disable())//csrf跨域访问无效
                      .sessionManagement(session -> session.maximumSessions(1).maxSessionsPreventsLogin(true));
              return httpSecurity.build();
          }
      }
    • 尝试访问/db/hello

    • 清除chrome浏览器的session数据。
      因为没有定义logout功能,所以每次login成功之后,都不能消除login情报,这时候可以在chrome浏览器直接使用快捷键ctrl-shift-del之后进行session情报的删除。这样能够进行测试。

2. 自定义认证表单

通常的情况是不用spring security默认提供的页面,下面进行自定义认证页面。

  1. 继续在SecurityConfigcontroller以及html中进行配置`

    • 配置自定义的认证url

      java 复制代码
       @Bean
          SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
              httpSecurity.authorizeHttpRequests(auth ->
                              auth.requestMatchers("/login*")
                                      .permitAll()
                                      .requestMatchers("/admin/**")//匹配所有/** url
                                      .hasRole("ADMIN")//只能对于admin的role,才能访问
                                      .requestMatchers("/user/**")//匹配/user/**
                                      .hasRole("USER")//只有对于user的role,才能访问
                                      .requestMatchers("/db/**")//配置/db/**
                                      .hasRole("DBA")//只有对于dba的role,才能访问
                                      .anyRequest()
                                      .authenticated()//设定任何访问都需要认证
                      )
                      .formLogin(form -> form.loginPage("/loginPage")
                              .loginProcessingUrl("/doLogin")//这里对于前后端分离,提供的非页面访问url
                              .usernameParameter("uname")//页面上form的用户名
                              .passwordParameter("passwd")
                              .successHandler(new SuccessHandler())//认证成功的处理
                              .failureHandler(new FailureHandler())//认证失败的处理
                              .defaultSuccessUrl("/index")//默认的认证之后的页面
                              .failureForwardUrl("/loginPasswordError"))//默认的密码失败之后的页面
                      .exceptionHandling(exceptionHandling ->
                              exceptionHandling.accessDeniedHandler(new CustomizeAccessDeniedHandler()))
                      .csrf(csrf -> csrf.disable())//csrf跨域访问无效
                      .sessionManagement(session -> session.maximumSessions(1).maxSessionsPreventsLogin(true));
              return httpSecurity.build();
          }
      • 对于认证画面的url,进行permitAll开放,因为对于认证画面,不需要进行认证。

        java 复制代码
        auth.requestMatchers("/login*")
        .permitAll()
      • 定义login的认证画面url,之后会定义controllerview 注意,仅限于前后端一体程序

        java 复制代码
        .formLogin(form -> form.loginPage("/loginPage")
      • 定于处理认证请求的url 注意前后端一体和前后端分离程序都会使用用这个url,springboot不对这个url定义controller

        java 复制代码
         .loginProcessingUrl("/doLogin")//这里对于前后端分离,提供的非页面访问url
      • 对自定义页面的input进行设定,这里之后的html会使用。

        java 复制代码
        .usernameParameter("uname")//页面上form的用户名
        .passwordParameter("passwd")
      • 对于前后端的分离应用,直接访问doLogin,通过这里给前端返回结果对这两个类详细说明。

        java 复制代码
         .successHandler(new SuccessHandler())//认证成功的处理
         .failureHandler(new FailureHandler())//认证失败的处理
      • 如果直接访问loginPage,那么认证成功后默认的url就是这里
        注意,和successForwardUrl的区别是,successForwardUrlpost请求,这里是get请求

        java 复制代码
        .defaultSuccessUrl("/index")//默认的认证之后的页面
      • 配置密码失败之后的url

        java 复制代码
        .failureForwardUrl("/loginPasswordError"))//默认的密码失败之后的页面
      • 配置没有权限时候的错误页面的url,之后对CustomizeAccessDeniedHandler类进行说明。

        java 复制代码
        exceptionHandling(exceptionHandling ->
          exceptionHandling.accessDeniedHandler(new CustomizeAccessDeniedHandler()))
      • success handler类进行定义,主要在前后端的程序中使用。

        java 复制代码
         //success handler
            private static class SuccessHandler implements AuthenticationSuccessHandler {
                @Override
                public void onAuthenticationSuccess(
                        HttpServletRequest httpServletRequest,
                        HttpServletResponse httpServletResponse,
                        Authentication authentication
                ) throws IOException {
                    Object principal = authentication.getPrincipal();
                    httpServletResponse.setContentType("application/json;charset=utf-8");
                    PrintWriter printWriter = httpServletResponse.getWriter();
                    httpServletResponse.setStatus(200);
                    Map<String, Object> map = new HashMap<>();
                    map.put("status", 200);
                    map.put("msg", principal);
                    ObjectMapper om = new ObjectMapper();
                    printWriter.write(om.writeValueAsString(map));
                    printWriter.flush();
                    printWriter.close();
                }
      • failure handler类进行定义,主要在前后端的程序中使用。

        java 复制代码
         //failure handler
            private static class FailureHandler implements AuthenticationFailureHandler {
                @Override
                public void onAuthenticationFailure(
                        HttpServletRequest httpServletRequest,
                        HttpServletResponse httpServletResponse,
                        AuthenticationException authenticationException
                ) throws IOException {
                    httpServletResponse.setContentType("application/json;charset=utf-8");
                    PrintWriter printWriter = httpServletResponse.getWriter();
                    httpServletResponse.setStatus(401);
                    Map<String, Object> map = new HashMap<>();
                    map.put("status", 401);
                    if (authenticationException instanceof LockedException) {
                        map.put("msg", "账户被锁定,登陆失败");
                    } else if (authenticationException instanceof BadCredentialsException) {
                        map.put("msg", "账户输入错误,登陆失败");
                    } else {
                        map.put("msg", "登陆失败");
                    }
                    ObjectMapper om = new ObjectMapper();
                    printWriter.write(om.writeValueAsString(map));
                    printWriter.flush();
                    printWriter.close();
                }
      • CustomizeAccessDeniedHandler类进行定义,对没有权限等情况进行定义。比如,需要的roleADMIN,但是认证结束后的role确是DBA

        java 复制代码
        private static class CustomizeAccessDeniedHandler implements AccessDeniedHandler {
                @Override
                public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
                    response.sendRedirect("/loginNoPermissionError");
                }
            }
    • controller层做出一个LoginController 注意,为了定义permitAll方便,统一采用login`开头

      java 复制代码
      @Controller
      public class LoginController {
          @GetMapping("/loginPage")
          public String loginPage() {
              return "login";
          }
      
          @GetMapping("/loginNoPermissionError")
          public String loginNoPermission() {
              return "no_permission_error";
          }
      
          @GetMapping("/loginPasswordError")
          public String loginError(Model model) {
              model.addAttribute("message", "认证失败");
              return "password_error";
          }
          @PostMapping("/loginPasswordError")
          public String loginErrorPost(Model model) {
              model.addAttribute("message", "认证失败");
              return "password_error";
          }
      }
    • 定义view层的各个html 注意,前后端分离程序

      • login的认证画面view

        html 复制代码
        <!DOCTYPE HTML>
        <html xmlns:th="http://www.thymeleaf.org/" lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Spring Security 用户自定义认证画面</title>
        </head>
        <body>
        <h1>自定义用户登陆</h1>
        <form th:action="@{/doLogin}" method="post">
            用户名:<input type="text" name="uname"><br>
            密码:<input type="text" name="passwd"><br>
            <input type="submit" value="登陆">
        </form>
        </body>
        </html>
      • 定义密码错误的认证错误画面view

        html 复制代码
        <!DOCTYPE HTML>
        <html xmlns:th="http://www.thymeleaf.org/" lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Spring Security 用户自定义-密码输入错误</title>
        </head>
        <body>
        <h1>自定义用户登陆错误-用户密码输入错误"</h1>
        </form>
        </body>
        </html>
      • 定义没有权限的认证错误画面view,比如需要ADMINrole,但是用户只有DBArole

        html 复制代码
        <!DOCTYPE HTML>
        <html xmlns:th="http://www.thymeleaf.org/" lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Spring Security 用户自定义-权限不足</title>
        </head>
        <body>
        <h1>"用户自定义画面,权限不足"</h1>
        </form>
        </body>
        </html>
  2. 验证结果

    • DBA用户访问http://localhost:8080/db/hello
    • 输入错误密码
    • USERrole用户登录,但是访问http://localhost:8080/db/hello,需要DBArole
相关推荐
爱码少年1 小时前
springboot中责任链模式之简单应用
spring boot·责任链模式
苹果酱05672 小时前
「Mysql优化大师一」mysql服务性能剖析工具
java·vue.js·spring boot·mysql·课程设计
武昌库里写JAVA2 小时前
【MySQL】7.0 入门学习(七)——MySQL基本指令:帮助、清除输入、查询等
spring boot·spring·毕业设计·layui·课程设计
eybk5 小时前
Pytorch+Mumu模拟器+萤石摄像头实现对小孩学习的监控
学习
6.945 小时前
Scala学习记录 递归调用 练习
开发语言·学习·scala
Aileen_0v05 小时前
【AI驱动的数据结构:包装类的艺术与科学】
linux·数据结构·人工智能·笔记·网络协议·tcp/ip·whisper
守护者1706 小时前
JAVA学习-练习试用Java实现“使用Arrays.toString方法将数组转换为字符串并打印出来”
java·学习
学会沉淀。6 小时前
Docker学习
java·开发语言·学习
Rinai_R6 小时前
计算机组成原理的学习笔记(7)-- 存储器·其二 容量扩展/多模块存储系统/外存/Cache/虚拟存储器
笔记·物联网·学习
吃着火锅x唱着歌6 小时前
PHP7内核剖析 学习笔记 第四章 内存管理(1)
android·笔记·学习