zoukankan      html  css  js  c++  java
  • 02 spring security 自定义用户认证流程

    1. 自定义登录页面

    (1)首先在static目录下面创建login.html

           注意: springboot项目默认可以访问resources/resources, resources/staic, resources/public目录下面的静态文件

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录页面</title>
    </head>
    <body>
    <form action="/auth/login" method="post">
        用户名:<input type="text" name="username">
        <br/>&emsp;码:<input type="password" name="password">
        <br/>
        <input type="submit" value="登录">
    </form>
    </body>
    </html>

    (2) 在spring securiy 配置类中做如下配置

      @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.formLogin()
                    // 指定自定义登录页面
                    .loginPage("/login.html")
                    // 登录url
                    .loginProcessingUrl("/auth/login")
                    .and()
                    .authorizeRequests()
                    // 添加一个url匹配器,如果匹配到login.html,就授权
                    .antMatchers("/login.html").permitAll()
                    .anyRequest()
                    .authenticated()
                    .and()
                    // 关闭spring security默认的防csrf攻击
                    .csrf().disable();
        }

    (3) 测试

    (4) 存在的问题

     <1> 作为可以复用的登录模块,我们应该提供个性化的登录页面,也就是说不能写死只跳转到login.html。

        此问题比较好解决,使用可配置的登录页面,默认使用login.html即可。

     <2> 请求跳转到login.html登录页面,貌似没有什么问题,但作为restful风格的接口,一般响应的都是json数据格式,尤其是app请求。

        解决思想: 用户发起数据请求 --> security判断是否需要身份认证 -----> 跳转到一个自定义的controller方法 ------> 在该方法内判断是否是html发起的请求,如果是,就跳转到login.html,如果不是,响应一个json格式的数据,说明错误信息。

    自定义Controller

    @Slf4j
    @RestController
    public class LoginController {
    
        /**
         * 请求缓存
         */
        private RequestCache requestCache = new HttpSessionRequestCache();
    
        /**
         * 重定向工具类
         */
        private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
    
        /**
         * 如果配置的登录页就使用配置的登录面,否则使用默认的登录页面
         */
    //    @Value("${xxxx:defaultLoginPage}")
    //    private String standardLoginPage;
        private String standardLoginPage = "/login.html";  // 登录页
    
        /**
         * 用户身份认证方法
         */
        @GetMapping("/user/auth")
        @ResponseStatus(code = HttpStatus.UNAUTHORIZED)  // 返回状态
        public ResponseData login(HttpServletRequest request, HttpServletResponse response) throws IOException {
            SavedRequest savedRequest = requestCache.getRequest(request, response);
            if (savedRequest != null) {
                String targetUrl = savedRequest.getRedirectUrl();
                log.info("请求是:" + targetUrl);
                // 如果请求是以html结尾
                if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) {
                    redirectStrategy.sendRedirect(request, response, standardLoginPage);
                }
            }
            return new ResponseData("该请求需要登录,js拿到我的响应数据后,是否需要跳转到登录页面你自己看着办吧?");
        }
    }

    spring security给该controller的login方法授权

     @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.formLogin()
                    // 先进controller中去
                    .loginPage("/user/auth")
                    // 指定自定义登录页面
                    .loginPage("/login.html")
                    // 登录url
                    .loginProcessingUrl("/auth/login")
                    .and()
                    .authorizeRequests()
                    // 该controller需要授权
                    .antMatchers("/user/auth").permitAll()
                    // 添加一个url匹配器,如果匹配到login.html,就授权
                    .antMatchers("/login.html").permitAll()
                    .anyRequest()
                    .authenticated()
                    .and()
                    // 关闭spring security默认的防csrf攻击
                    .csrf().disable();
        }

    这样子就行了!!!

       

    2.  自定义登录成功处理(返回json)

     (1)实现AuthenticationSuccessHandler.java

    import com.fasterxml.jackson.databind.ObjectMapper;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.MediaType;
    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;
    @Slf4j
    @Component
    public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
        @Autowired
        private ObjectMapper objectMapper;
        /**
         * Called when a user has been successfully authenticated.
         * @param request
         * @param response
         * @param authentication
         * @throws IOException
         * @throws ServletException
         */
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            log.info("登录成功!!!");
            // 将登录成功的信息写到前端
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.getWriter().write(objectMapper.writeValueAsString(authentication));
    
        }
    }

    (2)修改security 配置类

        @Autowired
        private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.formLogin()
                    // 先进controller中去
                    .loginPage("/user/auth")
                    // 指定自定义登录页面
                    .loginPage("/login.html")
                    // 登录url
                    .loginProcessingUrl("/auth/login")
                    .successHandler(myAuthenticationSuccessHandler)
                    .and()
                    .authorizeRequests()
                    // 该controller需要授权
                    .antMatchers("/user/auth").permitAll()
                    // 添加一个url匹配器,如果匹配到login.html,就授权
                    .antMatchers("/login.html").permitAll()
                    .anyRequest()
                    .authenticated()
                    .and()
                    // 关闭spring security默认的防csrf攻击
                    .csrf().disable();
        }

    (3)测试

     说明: authentication对象中包含的信息,会因为登录方式的不同而发生改变

    3. 自定义登录失败处理(返回json)

      实现AuthenticationFailureHandler.java 接口即可,跟登录成败处理配置一样。

    4. 自定义登录成功处理逻辑

     以上的登录成功或失败的返回的都是json,但是在某些情况下,就是存在着登录成功或者失败进行页面跳转(spring security默认的处理方式),那么这种返回json的方式就不合适了。 所以,我们应该做得更灵活,做成可配置的。

     对于登录成功逻辑而言只需要对MyAuthenticationSuccessHandler.java稍做修改就行,代码如下所示:

    /**
     * SavedRequestAwareAuthenticationSuccessHandler spring security 默认的成功处理器
     */
    @Slf4j
    @Component
    public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
        @Autowired
        private ObjectMapper objectMapper;
    
        /**
         * 配置的登录方式
         */
    //    @Value("${xxx:默认方式}")
        private String loginType = "JSON";
        /**
         * Called when a user has been successfully authenticated.
         */
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            log.info("登录成功!!!");
    
            // 如果配置的登录方式是JSON,就返回json数据
            if ("JSON".equals(loginType)) {
                // 将登录成功的信息写到前端
                response.setContentType(MediaType.APPLICATION_JSON_VALUE);
                response.getWriter().write(objectMapper.writeValueAsString(authentication));
            } else {  // 否则就使用默认的跳转方式
                super.onAuthenticationSuccess(request,response,authentication);
            }
        }
    }

    5. 自定义登录失败处理逻辑

     同登录成功类似,具体代码如下:

     

    import com.fasterxml.jackson.databind.ObjectMapper;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
    import org.springframework.security.core.AuthenticationException;
    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;
    @Slf4j
    @Component
    public class MySimpleUrlAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
        @Autowired
        private ObjectMapper objectMapper;
    
        /**
         * 配置的登录方式
         */
    //    @Value("${xxx:默认方式}")
        private String loginType = "JSON";
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
            log.info("登录失败!!!");
    
            // 如果配置的登录方式是JSON,就返回json数据
            if ("JSON".equals(loginType)) {
                // 将登录成功的信息写到前端
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
                response.setContentType(MediaType.APPLICATION_JSON_VALUE);
                response.getWriter().write(objectMapper.writeValueAsString(exception));
            } else {  // 否则就使用默认的跳转方式,跳转到一个错误页面
                super.onAuthenticationFailure(request,response,exception);
            }
        }
    }
     @Autowired
        private MySimpleUrlAuthenticationFailureHandler mySimpleUrlAuthenticationFailureHandler;
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.formLogin()
                    // 先进controller中去
                    .loginPage("/user/auth")
                    // 指定自定义登录页面
                    .loginPage("/login.html")
                    // 登录url
                    .loginProcessingUrl("/auth/login")
                    .successHandler(myAuthenticationSuccessHandler)
                    .failureHandler(mySimpleUrlAuthenticationFailureHandler)
                    .and()
                    .authorizeRequests()
                    // 该controller需要授权
                    .antMatchers("/user/auth").permitAll()
                    // 添加一个url匹配器,如果匹配到login.html,就授权
                    .antMatchers("/login.html").permitAll()
                    .anyRequest()
                    .authenticated()
                    .and()
                    // 关闭spring security默认的防csrf攻击
                    .csrf().disable();
        }
  • 相关阅读:
    Web开发模式演变(转)
    面向切面编程--AOP(转)
    Python装饰器与面向切面编程(转)
    python 线程,GIL 和 ctypes(转)
    Python性能鸡汤(转)
    对Django框架架构和Request/Response处理流程的分析(转)
    django源码笔记-【2】(转)
    django源码笔记-【1】(转)
    PHPCMS9.6.0最新版SQL注入和前台GETSHELL漏洞分析 (实验新课)
    从零开始学习渗透Node.js应用程序
  • 原文地址:https://www.cnblogs.com/z-qinfeng/p/11780022.html
Copyright © 2011-2022 走看看