Spring Security_自定义登录成功及失败处理_2020.06.02

学习基于记录,而不止于记录。

希望自己能坚持下去~

0.写在前面

    继续上一篇博客的内容,记录自定义登录成功和失败处理逻辑。
spring boot版本:2.2.7.RELEASE
开发工具:IntelliJ IDEA 2018.3.2 (Ultimate Edition)
jdk: java version "1.8.0_181"
maven: 3.3.9

1.自定义登录成功处理的实现

默认是要实现AuthenticationSuccessHandler接口,但是一般不使用这种方式,而是继承SavedRequestAwareAuthenticationSuccessHandler类,这个类继承的父类实际上已经实现了接口,另外子类功能更强大能够满足开发需求。

MyAuthenticationSuccessHandler.java
自定义的登录成功处理类

package com.grj.securityDemo.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.grj.securityDemo.model.MyResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 *  securit登录成功自定义处理类
 * @Author: grj
 * @Date: 2020/6/2 20:25
 */
@Component
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
    //从application.properties读取值存入
    @Value("${spring.security.loginType}")
    private String loginType;

    private static ObjectMapper mapper = new ObjectMapper();

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response,
                                        Authentication authentication) throws ServletException, IOException {
        //如果是json类型,则返回json数据,否则继续使用父类方法跳转登陆之前请求的页面
        if(loginType.equalsIgnoreCase("json")) {
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(mapper.writeValueAsString(MyResponse.getSuccessInstance()));
        }else {
            super.onAuthenticationSuccess(request,response,authentication);
        }

    }
}

MyResponse.java
这是我自定义的返回数据实体model,里面使用了Lombok依赖注解

package com.grj.securityDemo.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Author: grj
 * @Date: 2020/6/2 20:36
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class MyResponse {
    public enum ResponseTypeEnum {NULL, SUCCESS, ERROR, WARNING};

    private Integer code;
    private ResponseTypeEnum type;
    private String message;
    private Object data;

    public static MyResponse getSuccessInstance() {
        return MyResponse.builder()
                .code(200)
                .type(ResponseTypeEnum.SUCCESS)
                .data(null)
                .message("请求成功!")
                .build();
    }

    public static MyResponse getFailureInstance() {
        return MyResponse.builder()
                .code(500)
                .type(ResponseTypeEnum.ERROR)
                .data(null)
                .message("请求失败!")
                .build();
    }
}

SecurityConfig.java

package com.grj.securityDemo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @Author: grj
 * @Date: 2020/6/1 20:10
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //关闭这项拦截功能,要不然后面测试都无法进行
        http.csrf().disable();

        //登录任何人都可以访问
        //pageA和pageB只有拥有user或者admin权限才可以访问
        //syslog和sysuser只有拥有admin权限才可以访问
        http.formLogin()
                .loginPage("/login.html")
                .usernameParameter("username")
                .passwordParameter("password")
                .loginProcessingUrl("/login")
                .failureForwardUrl("/login.html")
                //注意defaultSuccessUrl和successHandler只能存在一种处理方式
//                .defaultSuccessUrl("/index")
                .successHandler(myAuthenticationSuccessHandler)
                .and()
                .authorizeRequests()
                .antMatchers("/login.html", "/login").permitAll()
                .antMatchers("/pageA.html", "pageB.html")
                .hasAnyAuthority("ROLE_user", "ROLE_admin")
                .antMatchers("/syslog.html", "/sysuser.html")
                //这两种方式写法是等同的
//                .hasAnyRole("admin")
                .hasAnyAuthority("ROLE_admin")
                .anyRequest().authenticated();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user")
                .password(passwordEncoder().encode("123456"))
                .roles("user")
                .and()
                .withUser("admin")
                .password(passwordEncoder().encode("123456"))
//                .authorities("/syslog", "/sysuser")
                .roles("admin")
                .and()
                .passwordEncoder(passwordEncoder());    //配置加密方式
    }

    //加密方式
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

application.properties

#security
spring.security.loginType=html

2.自定义登录成功处理的测试

1)json数据返回
application.properties

#security
spring.security.loginType=json
  • 浏览器打开http://localhost:8080/sysuser访问用户管理html

    image.png

  • 被拦截到登录页


    image.png
  • 以admin用户登录,登录成功,返回我之前定义的json数据


    image.png

2)html页面返回
application.properties

#security
spring.security.loginType=html

前几步一样,但是最后登录成功后不会返回json而是直接跳转我们想要访问的用户管理html页面,如下:


image.png

3.自定义登录失败处理的实现

很多操纵都是类似的,不多做赘述了。
MyAuthenticationFailureHandler.java

package com.grj.securityDemo.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.grj.securityDemo.model.MyResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 *  securit登录成功自定义处理类
 * @Author: grj
 * @Date: 2020/6/2 20:25
 */
@Component
public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
    //从application.properties读取值存入
    @Value("${spring.security.loginType}")
    private String loginType;

    private static ObjectMapper mapper = new ObjectMapper();

    @Override
    public void onAuthenticationFailure(HttpServletRequest request,
                                        HttpServletResponse response,
                                        AuthenticationException exception) throws IOException, ServletException {
        if(loginType.equalsIgnoreCase("json")) {
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(mapper.writeValueAsString(MyResponse.getFailureInstance()));
        }else {
            //会跳转到登录页
            super.onAuthenticationFailure(request,response,exception);
        }
    }
}

SecurityConfig.java部分代码

http.formLogin()
                .loginPage("/login.html")
                .usernameParameter("username")
                .passwordParameter("password")
                .loginProcessingUrl("/login")
//                .failureForwardUrl("/login.html")
                .failureHandler(myAuthenticationFailureHandler)
                //注意defaultSuccessUrl和successHandler只能存在一种处理方式
//                .defaultSuccessUrl("/index")
                .successHandler(myAuthenticationSuccessHandler)
                .and()
                .authorizeRequests()
                .antMatchers("/login.html", "/login").permitAll()
                .antMatchers("/pageA.html", "pageB.html")
                .hasAnyAuthority("ROLE_user", "ROLE_admin")
                .antMatchers("/syslog.html", "/sysuser.html")
                //这两种方式写法是等同的
//                .hasAnyRole("admin")
                .hasAnyAuthority("ROLE_admin")
                .anyRequest().authenticated();

4.自定义登录失败处理的测试

1)html
没啥好说的,就是登录失败还是跳回登录页

2)json
会出现以下的效果


image.png

5.总结

    实现登录验证结果的自定义处理,可以根据需求使用json或者html页面做出相应的反应,比如其后端分离常用的是json返回,并加以回应处理,一般前后端不分离直接采用页面跳转就可以了,当然根据实际需求选择。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容