• Spring Security3、使用JSON进行登录交互


    在前面的文章中,我们使用了Spring Security默认的登录页面,但是我们的前端小姐姐说登录页面太丑了,要换一个。于是我想,竟然都用springboot了,那我就用json来进行及交互吧,这样我们就不用管页面是什么样子的了。这样想换成什么样子的登录页面都可以,岂不美哉?

    在前面的配置文件中,我们重新了configure的一个方法,主要用于设置登录账号和密码的。现在我们需要再重新另外一个参数为HttpSecurity的configure方法,主要用于配置认证和授权的策略的。

    一、忽略指定路径

    在实际使用过程中,我们并不希望所有路径都需要登录以后才能访问,比如验证码接口,新闻接口这些,即使不登录也应该能正常请求数据,而不是跳转到登录去。

    这里我们就可以在configure方法中使用HttpSecurity的antMatchers方法设置忽略的地址。

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            // 设置忽略地址,全部允许直接访问,可以使用通配符
            .antMatchers("/code", "/login", "/logout", "/doLogin").permitAll()
            // 除忽略的地址,其他全部需要认证
            .anyRequest().authenticated()
            // 设置可以跨域
            .and().cors()
            // 禁用csrf
            .and().csrf().disable();
    }
    

    二、登录配置

    登录时我们需要3个主要操作,如何登录,登录成功返回数据,登录失败返回数据。在HttpSecurity这个类中我们就可以配置对应的参数来实现。

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/code", "/login", "/logout", "/doLogin").permitAll()
            .anyRequest().authenticated()
            
            // 设置表单登录
            .and().formLogin()
            // 登录表单的字段名
            .usernameParameter("username")
            .passwordParameter("password")
            // 设置登录的接口地址
            .loginProcessingUrl("/doLogin")
            // 登录成功处理
            .successHandler(successHandler)
            // 登录失败处理
            .failureHandler(failureHandler)
            
            .and().cors()
            .and().csrf().disable();
    }
    

    2.1 如何登录

    我们只需要请求 loginProcessingUrl 里面配置的登录接口,传入登录的信息即可进行登录。登录表单的字段名默认为 usernamepassword ,如果登录传过来的不是的话,可以使用 usernameParameter 和 ·passwordParameter 这两个方法来进行修改。

    2.2 登录成功处理

    successHandler 是处理登录成功时的处理程序,我们可在这个程序里,把默认的处理行为改成返回json字符串,前端即可根据返回的结果进行自行处理,后台也就不用管需要如何跳转这些了。

    现在我们创建一个JsonSuccessHandler类,需要的实现类AuthenticationSuccessHandler

    【这里类里面使用到了 hutool 工具包,需要自行引入一下】

    import cn.hutool.core.lang.Console;
    import cn.hutool.core.lang.Dict;
    import cn.hutool.core.util.CharsetUtil;
    import cn.hutool.extra.servlet.ServletUtil;
    import cn.hutool.http.ContentType;
    import cn.hutool.json.JSONUtil;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * 登录成功
     *
     * @author lixin
     */
    @Component
    public class JsonSuccessHandler implements AuthenticationSuccessHandler {
        @Override
        public void onAuthenticationSuccess(
                HttpServletRequest request,
                HttpServletResponse response,
                Authentication authentication
        ) throws IOException, ServletException {
            
            // authentication 登录成功的对象,默认用户名
            Console.log("登录成功,{}", authentication);
            
            // 这里拼装一个数据对象,用于返回给前端
            Dict res = Dict.create()
                    .set("code", 0)
                    .set("msg", "登录成功")
                    .set("data", authentication);
            
            // 设置一下输出json字符串时的编码
            String contentType = ContentType.JSON.toString(CharsetUtil.CHARSET_UTF_8);
            
            // 响应json字符串
            ServletUtil.write(response, JSONUtil.toJsonStr(res), contentType);
        }
    }
    

    2.3 登录失败处理

    failureHandlersuccessHandler的处理逻辑也是一样的,实现AuthenticationFailureHandler接口即可。

    import cn.hutool.core.lang.Console;
    import cn.hutool.core.lang.Dict;
    import cn.hutool.core.util.CharsetUtil;
    import cn.hutool.extra.servlet.ServletUtil;
    import cn.hutool.http.ContentType;
    import cn.hutool.json.JSONUtil;
    import org.springframework.security.authentication.*;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.security.web.authentication.AuthenticationFailureHandler;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * 登录失败
     *
     * @author lixin
     */
    @Component
    public class JsonFailureHandler implements AuthenticationFailureHandler {
        @Override
        public void onAuthenticationFailure(
                HttpServletRequest request, HttpServletResponse response,
                AuthenticationException exception
        ) throws IOException, ServletException {
            Console.log("登录失败,{}", exception);
            Dict res = Dict.create().set("code", 1000).set("msg", "登录失败");
            
            // 根据返回的异常类,确定登录失败的原因
            if (exception instanceof UsernameNotFoundException) {
                res.set("data", "用户名不存在");
            } else if (exception instanceof LockedException) {
                res.set("data", "账号被锁定");
            } else if (exception instanceof DisabledException) {
                res.set("data", "账号被禁用");
            } else if (exception instanceof CredentialsExpiredException) {
                res.set("data", "密码过期");
            } else if (exception instanceof AccountExpiredException) {
                res.set("data", "账号过期");
            } else if (exception instanceof BadCredentialsException) {
                res.set("data", "账号密码输入有误");
            } else {
                res.set("data", "登录失败(" + exception.getMessage() + ")");
            }
            
            // 响应json字符串
            String contentType = ContentType.JSON.toString(CharsetUtil.CHARSET_UTF_8);
            ServletUtil.write(response, JSONUtil.toJsonStr(res), contentType);
        }
    }
    

    2.4 配置登录成功和失败的处理程序

    实现了登录成功处理类登录失败处理,我们还需要把他们注入到配置文件中,然后在configure中设置到指定的钩子上。

    import com.miaopasi.securitydemo.config.security.handler.JsonFailureHandler;
    import com.miaopasi.securitydemo.config.security.handler.JsonSuccessHandler;
    import org.springframework.beans.factory.annotation.Autowired;
    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.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    
    /**
     * Security配置类,会覆盖yml配置文件的内容
     *
     * @author lixin
     */
    @EnableWebSecurity
    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        final JsonSuccessHandler successHandler;
        final JsonFailureHandler failureHandler;
    
        @Autowired
        public SecurityConfig(JsonSuccessHandler successHandler, JsonFailureHandler failureHandler) {
            this.successHandler = successHandler;
            this.failureHandler = failureHandler;
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .antMatchers("/code", "/login", "/logout", "/doLogin").permitAll()
                    .anyRequest().authenticated()
                    .and().formLogin()
                    .usernameParameter("username")
                    .passwordParameter("password")
                    .loginProcessingUrl("/doLogin")
                    .successHandler(successHandler)
                    .failureHandler(failureHandler)
                    .and().cors()
                    .and().csrf().disable();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication()
                    .passwordEncoder(new BCryptPasswordEncoder())
                    .withUser("admin")
                    .password(new BCryptPasswordEncoder().encode("123456"))
                    .roles("");
        }
    }
    

    2.5 测试登录

    项目启动成功后,使用psot的方式请求接口 /doLogin

    传入参数:username=admin&password=123456

    登录成功

    {
        "msg": "登录成功",
        "code": 0,
        "data": {
            "authenticated": true,
            "authorities": [
                {}
            ],
            "principal": {
                "credentialsNonExpired": true,
                "authorities": [
                    {}
                ],
                "enabled": true,
                "accountNonExpired": true,
                "username": "admin",
                "accountNonLocked": true
            },
            "details": {
                "sessionId": "9F53DC91AFB7C0706C65E875D677A0F8",
                "remoteAddress": "127.0.0.1"
            }
        }
    }
    

    传入参数:username=admin&password=1234567

    登录失败

    
    {
        "msg": "登录失败",
        "code": 1000,
        "data": "账号密码输入有误"
    }
    

    spring security系列文章请 点击这里 查看。
    这是代码 码云地址
    注意注意!!!项目是使用分支的方式来提交每次测试的代码的,请根据章节来我切换分支。

  • 相关阅读:
    ES 分组排序java实现
    java多线程中的死锁、活锁、饥饿、无锁都是什么鬼?
    Java 虚拟机对锁优化所做的努力
    Docker 核心概念、安装、端口映射及常用操作命令,详细到令人发指。
    20条最最常用的Linux命令讲解
    Linux查看系统配置常用命令
    全面认识Docker和基本指令
    25个深度学习开源数据集
    Docker 简单运用
    一小时学会C# 6
  • 原文地址:https://www.cnblogs.com/lixingwu/p/13282158.html
Copyright © 2020-2023  润新知