【Spring Security + OAuth2 + JWT入门到实战】4. 系统配置自定义登录页面
关于个性化配置全部在spring-security-browser项目的BrowserSecurityConfig完成。
自定义登录页面
创建登录页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>标准登录页面</title>
</head>
<body>
<h2>标准登录页面</h2>
<h3>表单登录</h3>
<form action="/authentication/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>
注意这里的路径:acrion="/authentication/form"
;路径是自定义的,
而UsernamePasswordAuthenticationFilter默认是处理/login
路径的登录请求。下面配置解决这个问题
public UsernamePasswordAuthenticationFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}
在BrowserSecurityConfig类重写configure方法
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置
}
package com.spring.security;
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 {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置
http
.formLogin()
.loginPage("/signIn.html")//登录页面路径
// 处理登录请求路径
.loginProcessingUrl("/authentication/form")
.and()
.authorizeRequests() // 授权配置
//不需要认证的路径
.antMatchers("/signIn.html").permitAll()
.anyRequest() // 所有请求
.authenticated() // 都需要认证
.and().csrf().disable();
}
}
启动项目测试:http://127.0.0.1:8080/hello
处理不同类型的请求
我们做的是一个可重复使用的框架登录页面可能是多个,现在解决这个问题
在spring-security-browser项目创建自定义Controller
package com.spring.security;
import com.spring.security.support.SimpleResponse;
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;
@RestController
public class BrowserSecurityController {
// 封装了引发跳转请求的工具类,从session中获取
private RequestCache requestCache = new HttpSessionRequestCache();
// spring的工具类:封装了所有跳转行为策略类
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
/**
* 需要身份认证时跳转到这里
*
* @param request
* @param response
* @return
* @throws IOException
*/
@RequestMapping("/authentication/require")
@ResponseStatus(code = HttpStatus.UNAUTHORIZED)
public SimpleResponse requirAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
// 如果有引发认证的请求
// spring 在跳转前应该会把信息存放在某个地方?
if (savedRequest != null) {
String targetUrl = savedRequest.getRedirectUrl();
System.out.println("引发跳转的请求:" + targetUrl);
// 如果是html请求,则跳转到登录页
if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) {
redirectStrategy.sendRedirect(request, response, "这里填写要跳转的页面先不写,等等从配置文件读取");
}
}
// 否则都返回需要认证的json串
return new SimpleResponse("访问的服务需要身份认证!");
}
}
在spring-security-browser项目创建返回值类
package com.spring.security.support;
import lombok.Data;
@Data
public class SimpleResponse {
private Object content;
public SimpleResponse(Object content) {
this.content = content;
}
}
解决自定义跳转的登录页面,读取yml
修改spring-security-demo项目application.yml
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1/auto_test?useUnicode=yes&characterEncoding=UTF-8&useSSL=false
username: root
password: 123456
hk:
security:
browser:
loginPage: /demoLogin.html #登录页面
关于系统配置的封装,SecurityProperties最外层封装包含BrowserProperties(浏览器相关系统配置),ValidateCodeProperties(验证码相关系统配置),OAuth2Properties(权限相关系统配置);
在spring-security-core项目新建SecurityProperties类:
package com.spring.security.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 安全属性
*/
@Data
@ConfigurationProperties(prefix = "hk.security")
public class SecurityProperties {
private BrowserProperties browser = new BrowserProperties();
}
同目录BrowserProperties类
package com.spring.security.properties;
import lombok.Data;
/**
* 浏览器的属性
*/
@Data
public class BrowserProperties {
/**
* 登录页面 默认登录页signIn.html
*/
private String loginPage = "/signIn.html";
}
要想让上面的配置生效还需要加一个SecurityCoreConfig类
在core项目新建:
package com.spring.security.config;
import com.spring.security.properties.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* 安全核心配置
* SecurityProperties 生效
*/
@Configuration
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityCoreConfig {
}
改造BrowserSecurityController读取系统配置
package com.spring.security;
import com.spring.security.properties.SecurityProperties;
import com.spring.security.support.SimpleResponse;
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;
@RestController
public class BrowserSecurityController {
// 封装了引发跳转请求的工具类,从session中获取
private RequestCache requestCache = new HttpSessionRequestCache();
// spring的工具类:封装了所有跳转行为策略类
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@Autowired
private SecurityProperties securityProperties;
/**
* 需要身份认证时跳转到这里
*
* @param request
* @param response
* @return
* @throws IOException
*/
@RequestMapping("/authentication/require")
@ResponseStatus(code = HttpStatus.UNAUTHORIZED)
public SimpleResponse requirAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
// 如果有引发认证的请求
// spring 在跳转前应该会把信息存放在某个地方?
if (savedRequest != null) {
String targetUrl = savedRequest.getRedirectUrl();
System.out.println("引发跳转的请求:" + targetUrl);
// 如果是html请求,则跳转到登录页
if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) {
redirectStrategy.sendRedirect(request, response, securityProperties.getBrowser().getLoginPage());
}
}
// 否则都返回需要认证的json串
return new SimpleResponse("访问的服务需要身份认证!");
}
}
现在demo项目系统配置登录页面为:demoLogin.html
demo项目新建demoLogin.html页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>demo登录页面</title>
</head>
<body>
<h2>demo登录页面</h2>
</body>
</html>
改造BrowserSecurityConfig类不拦截登录页
package com.spring.security;
import com.spring.security.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("/authentication/require")//登录页面路径
// 处理登录请求路径
.loginProcessingUrl("/authentication/form")
.and()
.authorizeRequests() // 授权配置
//不需要认证的路径
.antMatchers("/authentication/require","/signIn.html",securityProperties.getBrowser().getLoginPage()).permitAll()
.anyRequest() // 所有请求
.authenticated() // 都需要认证
.and().csrf().disable();
}
}
启动项目访问:http://127.0.0.1:8080/hello
访问:http://127.0.0.1:8080/index.html
注释掉系统配置:
#hk:
# security:
# browser:
# loginPage: /demoLogin.html #登录页面
重启项目访问:http://127.0.0.1:8080/index.html