zoukankan      html  css  js  c++  java
  • springsecurity开发基于表单的认证--个性化用户认证流程

    自定义登录页面        http.formLogin().loginPage("/signin.html") 
    自定义登录成功处理     AuthenticationSuccessHandler
    自定义登录失败处理     AuthenticationFailHandler
    

    最终实现的效果是,当访问html页面时会跳转登录页面,并自定义登录成功处理以及自定义登录失败的梳理,而当访问的是非html页面请求时则抛出异常

    1. 新建项目

    使用idea中的spring工具创建并引入springbootspringsecruity

    最终的pom.xml代码如下

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.2.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.example</groupId>
        <artifactId>demo-spring-secruity</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>demo-spring-secruity</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>3.9</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.10</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    编写SimpleResponse用于处理输出异常信息的接口

    package com.example.demospringsecruity.support;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.ToString;
    
    /**
     *  一个简单的响应对象
     * @author john
     * @date 2020/1/6 - 16:23
     */
    @Data
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    public class SimpleResponse {
        private Object content;
    }
    
    

    2. 配置自定义登录

    通过继承WebSecurityConfigurerAdapter实现自定义配置

    package com.example.demospringsecruity.config;
    
    import com.example.demospringsecruity.handle.MyAuthenticationFailureHandler;
    import com.example.demospringsecruity.handle.MyAuthenticationSuccessHandler;
    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.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    /**
     * @author john
     * @date 2020/1/6 - 10:07
     */
    @Configuration
    public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
        @Autowired
        MyAuthenticationFailureHandler myAuthenticationFailureHandler;
    
        //手动将PasswordEncoder注入到ioc容器中
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // 表单登录
            http.formLogin()
                    .loginPage("/auth/require")     //设置登录路由
                    .loginProcessingUrl("/auth/form")  //设置登录处理url
                    .successHandler(myAuthenticationSuccessHandler) // 设置自定义成功处理器
                    .failureHandler(myAuthenticationFailureHandler) // 设置自定义失败处理器
                    .and()
                    .authorizeRequests()     // 身份认证设置
                    .antMatchers("/signin.html").permitAll() //该路由不需要身份认账
                    .antMatchers("/auth/*").permitAll() //该路由不需要身份认账
                    .anyRequest()       //其他的路由均需要身份认证
                    .authenticated()
                    .and()
                    .csrf()
                    .disable();   //先禁用防止跨站脚本攻击的csrf token
        }
    
    }
    

    通过实现UserDetailsService接口来实现自定义用户认证逻辑

    package com.example.demospringsecruity.config;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.authority.AuthorityUtils;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.stereotype.Component;
    
    /**
     * @author john
     * @date 2020/1/6 - 10:32
     */
    @Component
    @Slf4j
    public class MyUserDetailsService implements UserDetailsService {
        //这里可以注入mapper或者repository的dao对象来实现数据校验逻辑操作
        @Autowired
        private PasswordEncoder passwordEncoder;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            log.info("用户名:" + username);
            //这里密码应该从数据库中取出,暂时先使用加密生成
            String password = passwordEncoder.encode("123456");
            return new User(username,
                    password,
                    true,                 // 账户是否可用
                    true,        // 账户是否过期
                    true,     // 密码是否过期
                    true,        //账户是否被锁定
                    AuthorityUtils.commaSeparatedStringToAuthorityList("admin") //授权集合
            );
        }
    }
    

    编写一个自定义的登录页面

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录页面</title>
    </head>
    <body>
    <h2>标准登录页面</h2>
    <h3>表单登录</h3>
    <form action="/auth/form" method="post">
        <table>
            <tr>
                <td>用户名:</td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td>密码:</td>
                <td><input type="password" name="password"></td>
            </tr>
            <tr>
                <td colspan="2">
                    <button type="submit">登录</button>
                </td>
            </tr>
        </table>
    </form>
    </body>
    </html>
    

    3. 编写访问逻辑

    当访问html页面时会跳转登录页面,而当访问的是非html页面请求时则抛出异常

    package com.example.demospringsecruity.controller;
    
    import com.example.demospringsecruity.support.SimpleResponse;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.http.HttpStatus;
    import org.springframework.security.web.DefaultRedirectStrategy;
    import org.springframework.security.web.RedirectStrategy;
    import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
    import org.springframework.security.web.savedrequest.RequestCache;
    import org.springframework.security.web.savedrequest.SavedRequest;
    
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseStatus;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * @author john
     * @date 2020/1/6 - 16:03
     */
    @RestController
    @Slf4j
    public class BrowserSecruityController {
    
        private RequestCache requestCache = new HttpSessionRequestCache();
    
        private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
    
        //当需要身份认证时,跳转到这---如果是html页面请求跳转到登录页面,否则会被当成ajax异步请求,抛出异常
        @RequestMapping("/auth/require")
        @ResponseStatus(code = HttpStatus.UNAUTHORIZED)
        public SimpleResponse requireAuth(HttpServletRequest request, HttpServletResponse response) throws IOException {
            //获取前一次的跳转请求
            SavedRequest savedRequest = requestCache.getRequest(request, response);
            if (savedRequest != null) {
                String targetUrl = savedRequest.getRedirectUrl();
                log.info("引发跳转请求的url是:" + targetUrl);
                if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) {
                    redirectStrategy.sendRedirect(request, response, "/signin.html");
                }
            }
    
            return new SimpleResponse("访问的服务需要身份认证,请引导用户到登录页");
        }
    }
    

    4. 编写自定义登录成功处理

    package com.example.demospringsecruity.handle;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    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 john
     * @date 2020/1/6 - 17:20
     */
    @Component
    @Slf4j
    public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
        @Autowired
        private ObjectMapper objectMapper;
    
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            log.info("登录成功");
            //这里处理登录成功后就会json输出authentication里面的数据
            response.setContentType("application/json;charset-UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(authentication));
        }
    }
    

    5. 编写自定义登录失败处理

    package com.example.demospringsecruity.handle;
    
    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.security.core.AuthenticationException;
    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 john
     * @date 2020/1/6 - 18:43
     */
    @Component
    @Slf4j
    public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
        @Autowired
        private ObjectMapper objectMapper;
    
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
            log.info("登录失败");
            //这里处理登录失败后就会输出错误信息
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            response.setContentType("application/json;charset-UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(e));
        }
    }
    
    

    6. 测试

    6.1 测试访问<http://localhost:8081/aa>

    6.2 测试访问<http://localhost:8081/index.html>

    登录成功

    登录失败

    7. 代码资源

    链接:https://share.weiyun.com/50cUdga 密码:k2r9ie

  • 相关阅读:
    PHP学习心得(八)——运算符
    PHP学习心得(九)——函数
    PHP学习心得(十)——控制结构
    PHP学习心得(七)——常量
    PHP学习心得(六)——变量
    PHP学习心得(五)——类型
    PHP学习心得(四)——基本语法
    PHP学习心得(三)——处理表单
    PHP学习心得(二)——实用脚本
    PHP学习心得(一)——简介
  • 原文地址:https://www.cnblogs.com/ifme/p/12157999.html
Copyright © 2011-2022 走看看