SpringSecurity之个性化认证

自定义登录页面以及返回值确定

  • 默认登录页面
    login.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录页面</title>
    </head>
    <body>
    <h3>登录页面</h3>
    <form action="/login" 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>
                    <button type="submit">登录</button>
                </td>
            </tr>
        </table>
    </form>
    </body>
    </html>
    
  • 配置登录页面代替默认的登录页面

    package com.cong.security.browser;
    
    import com.cong.security.core.constant.SecurityConstant;
    import com.cong.security.core.properties.SecurityProperties;
    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.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    @Configuration
    public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private SecurityProperties securityProperties;
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // 使用表单登录进行身份认证
            http.formLogin()
                    .loginPage(SecurityConstant.DEFAULT_UNAUTHENTICATION_URL)// 自定义登录页面
                    .loginProcessingUrl("/login")// 自定义用户名密码登录请求路径
                    .and()
                    .authorizeRequests()
                    .antMatchers(SecurityConstant.DEFAULT_UNAUTHENTICATION_URL, securityProperties.getBrowser().getLoginPage()).permitAll() // 放开,防止反复重定向
                    .anyRequest() // 任何请求
                    .authenticated()// 需要身份认证
                    .and()
                    .csrf().disable(); //跨站请求伪造
        }
    }
    

    对配置中的默认值进行处理(常量,当yml文件未配置时使用如下默认参数)

    package com.cong.security.core.constant;
    
    public interface SecurityConstant {
    	/**
    	 * 当请求需要身份认证时,默认跳转的url
    	 */
    	public static final String DEFAULT_UNAUTHENTICATION_URL = "/auth/require";
    	/**
    	 * 默认登录页面
    	 */
    	public static final String DEFAULT_LOGIN_PAGE_URL = "/login.html";
    }
    

    Properties配置,如图:
    在这里插入图片描述
    类关系图:
    在这里插入图片描述
    有些是目前不需要使用的,一起配置上来了。
    SecurityProperties.java

    package com.cong.security.core.properties;
    
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    /**
     * 安全配置<br>
     * ConfigurationProperties表明当前类会读取application.yml配置文件中以my.security开头的参数<br>
     *
     * @author single-聪
     */
    @Data
    @ConfigurationProperties(prefix = "my.security")
    public class SecurityProperties {
        // 浏览器配置
        private BrowserProperties browser = new BrowserProperties();
        // 验证码配置
        private ValidateCodeProperties code = new ValidateCodeProperties();
        // 三方登录配置
        private SocialProperties social = new SocialProperties();
        // oAuth2配置
        private OAuth2Properties oAuth2 = new OAuth2Properties();
    }
    

    BrowserProperties.java

    package com.cong.security.core.properties;
    
    import com.cong.security.core.constant.LoginType;
    import com.cong.security.core.constant.SecurityConstant;
    import lombok.Data;
    
    @Data
    public class BrowserProperties extends SecurityProperties {
    
    	// 默认注册页
    	private String signUpUrl = "/regist.html";
    
    	// 
    	private String logoutUrl = "";
    
    	// 登录页
    	private String loginPage = SecurityConstant.DEFAULT_LOGIN_PAGE_URL;
    
    	// 默认返回json
    	private LoginType loginType = LoginType.JSON;
    }
    

    枚举

    package com.cong.security.core.constant;
    // 根据请求类型决定返回JSON还是重定向,实际开发中一般是返回JSON,HTML开发不多了。
    public enum LoginType {
    	REDIRECT, JSON
    }
    

    其他的类推即可,配置SecurityPropertiesConfig使SecurityProperties能够读取yml文件:

    package com.cong.security.core.config;
    
    import com.cong.security.core.properties.SecurityProperties;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * @Description 使SecurityProperties配置生效
     * @Author single-聪
     * @Date 2020/1/7 23:51
     * @Version 1.0.1
     **/
    @Configuration
    @EnableConfigurationProperties(SecurityProperties.class)
    public class SecurityPropertiesConfig {
    
    }
    

    注意,如果上述配置启动时报错Constructor threw exception; nested exception is java.lang.StackOverflowError,那么将各个Properties文件注解中@Data改为@Getter和@Setter,同时所有属性不需要new对象。

    定义BrowserSecurityController处理返回值

    package com.cong.security.browser;
    
    import com.cong.security.core.constant.SecurityConstant;
    import com.cong.security.core.properties.SecurityProperties;
    import com.cong.security.core.support.SimpleResponse;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    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;
    
    /**
     * @Description TODO
     * @Author single-聪
     * @Date 2020/1/7 23:28
     * @Version 1.0.1
     **/
    @Slf4j
    @RestController
    public class BrowserSecurityController {
    
        /*请求缓存*/
        private RequestCache requestCache = new HttpSessionRequestCache();
    
        private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
    
        @Autowired
        private SecurityProperties securityProperties;
    
        /**
         * 当需要身份证认证时,跳转到当前接口
         *
         * @param request
         * @param response
         * @return
         * @throws IOException
         */
        @RequestMapping(SecurityConstant.DEFAULT_UNAUTHENTICATION_URL)
        @ResponseStatus(code = HttpStatus.UNAUTHORIZED)// 返回状态码401
        public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response)
                throws IOException {
            // 拿到引发跳转的请求
            SavedRequest savedRequest = requestCache.getRequest(request, response);
            if (savedRequest != null) {
                // 引发跳转请求的url
                String target = savedRequest.getRedirectUrl();
                log.info("引发跳转请求的url是:[{}]", target);
                // 以.html结尾,直接跳转到登录页上
                if (StringUtils.endsWith(target, ".html")) {
                    redirectStrategy.sendRedirect(request, response, securityProperties.getBrowser().getLoginPage());
                }
            }
            // 不是html请求,返回401状态码,提示语无所谓
            return new SimpleResponse("用户未登录或权限不足");
        }
    }
    

    SimpleResponse是自定义的一个返回数据封装:

    package com.cong.security.core.support;
    import lombok.Data;
    @Data
    public class SimpleResponse {
    	private String info;
    	private Object content;
    	public SimpleResponse(Object content) {
    		super();
    		this.info = "error";
    		this.content = content;
    	}
    }
    
  • 登录测试
    在这里插入图片描述
    请求不以html结尾,返回JSON,以HTML结尾,重定向到登录页面。
    但是以我所知的目前的开发中使用到HTML页面的不是太多,基本都是脚手架开发,不会有HTML的东西,所以这一部分的笔记比较粗糙。

  • yml文件信息读取
    在这里插入图片描述
    需要配置@EnableConfigurationProperties注解使配置类生效。

  • 自定义登录成功处理
    默认是跳转回原请求
    定义成功处理器(browser模块)

    package com.cong.security.browser.authentication;
    
    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import com.cong.security.core.constant.LoginType;
    import com.cong.security.core.properties.SecurityProperties;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
    import org.springframework.stereotype.Component;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * 成功处理函数
     *
     * @author single-聪
     */
    @Slf4j
    @Component("myAuthenticationSuccessHandler")
    public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
    
        @Autowired
        private SecurityProperties securityProperties;
        /*工具类,将authentication转换成为json*/
        @Autowired
        private ObjectMapper objectMapper;
        /**
         * Authentication封装认证信息
         */
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                            Authentication authentication) throws IOException, ServletException {
            // 配置返回json
            if (LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())) {
                log.info("返回JSON数据");
                response.setContentType("application/json;charset=UTF-8");
                response.getWriter().write(objectMapper.writeValueAsString(authentication));
            } else {
                // 不是json,调用父类方法(跳转)
                super.onAuthenticationSuccess(request, response, authentication);
            }
        }
    }
    

    配置BrowserSecurityConfig使自定义处理器生效:
    在这里插入图片描述
    此时用户名密码登陆成功之后返回JSON,如下图:在这里插入图片描述

  • 自定义登录失败处理
    定义成功处理器(browser模块)

    package com.cong.security.browser.authentication;
    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import com.cong.security.core.constant.LoginType;
    import com.cong.security.core.properties.SecurityProperties;
    import com.cong.security.core.support.SimpleResponse;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
    import org.springframework.stereotype.Component;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    @Component("myAuthenticationFailureHandler")
    public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
    	/*工具类,将authentication转换成为json*/
    	@Autowired
    	private ObjectMapper objectMapper;
    	@Autowired
    	private SecurityProperties securityProperties;
    	@Override
    	public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
    			AuthenticationException exception) throws IOException, ServletException {
    		log.info("失败处理器");
    		if (LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())) {
    			// 状态码500
    			response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
    			response.setContentType("application/json;charset=UTF-8");
    			response.getWriter().write(objectMapper.writeValueAsString(new SimpleResponse(exception.getMessage())));
    		} else {
    			// 不是json,调用父类方法(跳转)
    			super.onAuthenticationFailure(request, response, exception);
    		}
    	}
    }
    

    配置BrowserSecurityConfig使自定义处理器生效,和成功处理器一样配置
    此时输入错误的密码进行登录,返回值为:
    在这里插入图片描述
    断点调试可以看到此时的异常类型为BadCredentialsException:
    在这里插入图片描述
    最后看一下yml配置文件格式:

    my:
      security:
        browser:
          login-page: /login.html
          login-type: JSON
          sign-up-url: /regist.html
        code:
          image:
            width: 67
            height: 23
            length: 4
            expire-in: 60
            url: /qqq
          sms:
            length: 6
            expire-in: 300
            url: /111
        social:
          filter-processes-url: /222
        oAuth2:
          jwt-signing-key: mydev
          store-type: jwt
    
发布了43 篇原创文章 · 获赞 25 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/single_cong/article/details/103912147