• Spring Security认证配置(三)


    学习本章之前,可以先了解下上篇Spring Security认证配置(二)

    本篇想要达到这样几个目的:

    1、登录成功处理

    2、登录失败处理

    3、调用方自定义登录后处理类型

    具体配置代码如下:

    spring-security-browser

    登录成功处理:

    /**
     * 自定义登录成功后处理
     */
    @Slf4j
    @Component
    public class LoginSuccessHandler implements AuthenticationSuccessHandler {
    
        @Autowired
        private ObjectMapper objectMapper;
    
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                Authentication authentication) throws IOException, ServletException {
            log.info("登录成功...");
    
            response.setContentType("application/json;charset=UTF-8");
            // 返回authentication 认证信息
            response.getWriter().write(objectMapper.writeValueAsString(authentication));
        }
    
    }

    这段配置表示,登录成功后,会向response中塞入authentication对象

    代码解析:

    点开 AuthenticationSuccessHandler 类,可以看到里面只有一个onAuthenticationSuccess方法,如下:

    onAuthenticationSuccess方法中,有个Authentication对象,其用法可参考AuthenticationManager、ProviderManager

    登录失败处理:

    /**
     * 自定义登录失败后处理
     */
    @Slf4j
    @Component
    public class LoginFailureHandler implements AuthenticationFailureHandler {
    
        @Autowired
        private ObjectMapper objectMapper;
    
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                AuthenticationException exception) throws IOException, ServletException {
            log.info("登录失败");
    
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); // 服务器内部异常500
            response.setContentType("application/json;charset=UTF-8");
            // 返回异常信息
            response.getWriter().write(objectMapper.writeValueAsString(exception));
        }
    
    }

     这段配置表示,登录失败后,会向response中塞入AuthenticationException 对象,并返回500状态码

    代码解析:

    AuthenticationFailureHandler 中也只有一个onAuthenticationFailure方法,其入参 AuthenticationException 是认证授权过程中抛出的异常类的基类,

     有以下几个子类:

    比较常用的是 BadCredentialsException(密码错误)、UsernameNotFoundException(用户名找不到)等等

    修改下SecurityConfig类中的配置

      @Autowired
        private SecurityProperty securityProperty;
        
        @Autowired
        private LoginSuccessHandler loginSuccessHandler; //登录成功处理
        
        @Autowired
        private LoginFailureHandler loginFailureHandler; //登录失败处理
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // formLogin()是默认的登录表单页,如果不配置 loginPage(url),则使用 spring security
            // 默认的登录页,如果配置了 loginPage()则使用自定义的登录页
            http.formLogin() // 表单登录
                .loginPage(SecurityConst.AUTH_REQUIRE) 
                .loginProcessingUrl(SecurityConst.AUTH_FORM) // 登录请求拦截的url,也就是form表单提交时指定的action
                .successHandler(loginSuccessHandler)
                .failureHandler(loginFailureHandler)
                .and()
                .authorizeRequests() // 对请求授权
                .antMatchers(SecurityConst.AUTH_REQUIRE, securityProperty.getBrowser().getLoginPage()).permitAll() // 允许所有人访问login.html和自定义的登录页
                .anyRequest() // 任何请求
                .authenticated()// 需要身份认证
                .and()
                .csrf().disable() // 关闭跨站伪造
            ;
        }

    启动服务访问 http://localhost:18081/index.html,跳转到登录页,输入正确的用户名密码(xwj/1234),跳转到如下页面:

    查看控制台,可以看到Authentication对象包含的数据:

    清掉浏览器缓存,然后再输入错误的密码,跳转到如下页面:

    查看控制台,可以看到AuthenticationException对象包含的数据:

    上面配置了登录成功和失败的处理,但是只能够返回JSON格式的数据。如果希望返回一个页面或者是跳转,那就可以使用Security默认的处理方式

    在Security中,默认的成功跟失败处理分别是 SavedRequestAwareAuthenticationSuccessHandlerSimpleUrlAuthenticationFailureHandler

    通过分析 AbstractAuthenticationProcessingFilter 类,可以看到:

    在服务启动时,如果在我们的配置类中,没有配置这AuthenticationSuccessHandler跟AuthenticationFailureHandler,则使用这两个默认的。如果配置了,则使用配置中

    定义的登录处理类。打个断点可以看到:

    修改下上面的配置代码:

    spring-security-browser

    为了达到调用方自定义跳转方式,增加配置类,如下:

    /**
     * 登录类型枚举
     */
    public enum LoginType {
    
        REDIRECT,
    
        JSON
    
    }

    修改BrowserProperty,增加默认的登录类型(JSON):

    @Getter
    @Setter
    public class BrowserProperty {
    
        private String loginPage = "/login.html"; // 登录页
        
        private LoginType loginType = LoginType.JSON; //登录类型
    
    }

    接下来修改登录与失败处理类:

    /**
     * 自定义登录成功后处理
     * (继承SavedRequestAwareAuthenticationSuccessHandler,这是默认的成功处理器,其带有requestCache)
     */
    @Slf4j
    @Component
    public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
    
        @Autowired
        private ObjectMapper objectMapper;
    
        @Autowired
        private SecurityProperty securityProperty;
    
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                Authentication authentication) throws IOException, ServletException {
            log.info("登录成功...");
    
            if (LoginType.JSON.equals(securityProperty.getBrowser().getLoginType())) { // json请求(如ajax)
                response.setContentType("application/json;charset=UTF-8");
                // 返回authentication 认证信息
                response.getWriter().write(objectMapper.writeValueAsString(authentication));
            } else { // 跳转
                super.onAuthenticationSuccess(request, response, authentication);
            }
        }
    
    }
    /**
     * 自定义登录失败后处理 
     * (继承SimpleUrlAuthenticationFailureHandler,这是默认的失败处理器)
     */
    @Slf4j
    @Component
    public class LoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {
    
        @Autowired
        private ObjectMapper objectMapper;
    
        @Autowired
        private SecurityProperty securityProperty;
    
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                AuthenticationException exception) throws IOException, ServletException {
            log.info("登录失败");
    
            if (LoginType.JSON.equals(securityProperty.getBrowser().getLoginType())) { // json请求(如ajax)
                response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); // 服务器内部异常500
                response.setContentType("application/json;charset=UTF-8");
                // 返回异常信息
                response.getWriter().write(objectMapper.writeValueAsString(exception));
            } else {
                super.onAuthenticationFailure(request, response, exception);
            }
        }
    
    }

    spring-security-demo

    在配置文件(application.yml)中,加上:

    security:
      browser: 
        #loginPage: /plogin.html
        loginType: REDIRECT

    重新启动服务,访问 http://localhost:18081/index.html,跳转到登录页,输入正确的用户名密码(xwj/1234),跳转到具体的页面:

    清掉浏览器缓存,然后再输入错误的密码,跳转到如下页面:

    从上面可以看到,返回401状态码,类型为Unauthorized,表示未认证。这里的????其实是认证失败原因,打个断点可以看到:

    抛出了BadCredentialsException异常,"坏的凭证"表示密码错误

  • 相关阅读:
    spring data实现自定义的repository实现类,实现跟jpa联通
    SQLYog快捷键大全
    JSP 中EL表达式用法详解
    java中的标记接口
    单元测试中快速执行某个方法
    Spring Boot 以 jar 包方式运行在后台
    Spring Data JPA
    扩展spring data jpa的数据更新方法时注意事项
    在Spring Data JPA 中使用Update Query更新实体类
    高速创建和mysql表相应的java domain实体类
  • 原文地址:https://www.cnblogs.com/xuwenjin/p/9645896.html
Copyright © 2020-2023  润新知