自定义登录页面以及返回值确定
-
默认登录页面
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.javapackage 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