1. 自定义登陆页面html
(1)首先在static目录下面建立login.html前端
注意: springboot项目默承认以访问resources/resources, resources/staic, resources/public目录下面的静态文件java
<!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/> 密 码:<input type="password" name="password"> <br/> <input type="submit" value="登陆"> </form> </body> </html>
(2) 在spring securiy 配置类中作以下配置web
@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) 测试spring
略json
(4) 存在的问题缓存
<1> 做为能够复用的登陆模块,咱们应该提供个性化的登陆页面,也就是说不能写死只跳转到login.html。springboot
此问题比较好解决,使用可配置的登陆页面,默认使用login.html便可。restful
<2> 请求跳转到login.html登陆页面,貌似没有什么问题,但做为restful风格的接口,通常响应的都是json数据格式,尤为是app请求。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(); }