Spring Security入门(十七)-个性化用户认证流程(一)
一.导学
- 自定义登录页面
- 自定义登录成功处理器
- 自定义登录失败处理器
二.自定义登录页面
- loginPage方法:指定登录页面的url
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()//form表单 高版本中默认是表单 低版本中默认是HttpBasic
.loginPage("/playmaker-signIn.html") //指定登录页面的url
//httpBasic() //httpBasic
.and()
//对请求授权配置
.authorizeRequests()
.anyRequest()//对任意请求都必须是已认证才能访问
.authenticated();
}
- playmaker-signIn.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>标准登录页面</title>
</head>
<body>
<h1>标准登录页面</h1>
<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>
- 这里写的处理请求路径是自定义的,表单登录是由UsernamePasswordAuthenticationFilter控制的 它控制的是login POST请求,所以加条代码
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()//form表单 高版本中默认是表单 低版本中默认是HttpBasic
.loginPage("/playmaker-signIn.html") //指定登录页面的url
.loginProcessingUrl("/authentication/form") //指定处理登录请求的url
//httpBasic() //httpBasic
.and()
//对请求授权配置
.authorizeRequests()
.anyRequest()//对任意请求都必须是已认证才能访问
.authenticated();
}
- 访问登录页会出现这个问题,因为你后边的代码,任何请求都需要认证,包括你的登录页面,于是又跳到自己页面去了,不断死循环去了
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()//form表单 高版本中默认是表单 低版本中默认是HttpBasic
.loginPage("/playmaker-signIn.html") //指定登录页面的url
.loginProcessingUrl("/authentication/form") //指定处理登录请求的url
//httpBasic() //httpBasic
.and()
//对请求授权配置
.authorizeRequests()
.antMatchers("/playmaker-signIn.html").permitAll()//当访问这个url时不需要身份认证
.anyRequest()//对任意请求都必须是已认证才能访问
.authenticated();
}
- 视频中此时访问登陆进去会报错403,关于csrf 跨站请求的东西(后边会讲)
- 然而我们并没有出现这个问题,但还是把它关了吧,完整配置代码如下
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()//form表单 高版本中默认是表单 低版本中默认是HttpBasic
.loginPage("/playmaker-signIn.html") //指定登录页面的url
.loginProcessingUrl("/authentication/form") //指定处理登录请求的url 会认为必须是跳转到我们自己写的这个真是存在的控制器里面,
实际上这个路径对应的控制器不存在也没有关系。因为不会走。这里看起来更像是对security默认/login路径的重命名
//httpBasic() //httpBasic
.and()
//对请求授权配置
.authorizeRequests()
.antMatchers("/playmaker-signIn.html").permitAll()//当访问这个url时不需要身份认证
.anyRequest()//对任意请求都必须是已认证才能访问
.authenticated()
.and()
.csrf().disable();//关闭csrf
}
此时访问OK
但是这里有两个问题
- 我们发的是RESTful请求 如果这个请求需要身份认证 那么返回去的是一段html 这个是不合理的 RESTful服务返回去得应该是状态码和json信息,但是现在实际上是html(如果是html请求跳到登录页上 如果不是就返回一段json对象)
- 虽然写了一个标准登陆页面 但我们的目标是写一个可重用的安全模块 意味着多个项目要运行这个安全模块 如何让使用这个安全模块的项目能够自定义这个模块 不用的时候才用你这个标准安全模块
三.处理不同类型的请求
@RestController
public class BorowserSecurityController {
private Logger logger = LoggerFactory.getLogger(getClass());
private RequestCache requestCache = new HttpSessionRequestCache();//跳转之前spring security用HttpSessionRequestCache这个类把当前请求缓存到这个session里面去 所以当我们跳转到authentication/require这个请求时我们可以用这个类,从session中把之前缓存的请求拿出来
@Autowired
private SecurityProperties securityProperties;
@RequestMapping("/authentication/require")
@ResponseStatus(code= HttpStatus.UNAUTHORIZED) //401
public SimpleResponse authenticationrequire(HttpServletRequest request, HttpServletResponse response) throws IOException {
SavedRequest savedRequest = requestCache.getRequest(request,response);//SavedRequest就是之前缓存的请求
if(savedRequest!=null){
String redirectUrl = savedRequest.getRedirectUrl();
logger.info("跳转的地址:"+redirectUrl);
if(StringUtils.endsWithIgnoreCase(redirectUrl,".html")){
redirectStrategy.sendRedirect(request,response,"");//这个跳转url是第二个要处理的问题
}
}
return new SimpleResponse("访问的服务需要身份认证,请引导用户到登录页");
}
}
- SimpleResponse 类只是一个返回结果的信息类
public class SimpleResponse {
private Object content;//定义成Object可以返回任何数据类型
public SimpleResponse(Object content) {
this.content = content;
}
public Object getContent() {
return content;
}
public void setContent(Object content) {
this.content = content;
}
}
- 我们把登录页面url换成/authentication/require
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()//form表单 高版本中默认是表单 低版本中默认是HttpBasic
.loginPage("/authentication/require") //指定登录页面的url
.loginProcessingUrl("/authentication/form") //指定处理登录请求的url
//httpBasic() //httpBasic
.and()
//对请求授权配置
.authorizeRequests()
.antMatchers("/playmaker-signIn.html",
"/authentication/require").permitAll()//当访问这个url时不需要身份认证
.anyRequest()//对任意请求都必须是已认证才能访问
.authenticated()
.and()
.csrf().disable();//关闭csrf
}
四.自定义安全模块
- 让用户配置自己登录页面
- application.properties
playmaker.security.browser.loginPage = /demo-signIn.html
- demo-signIn.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Demo登录页</title>
</head>
<body>
<h1>Demo登录页</h1>
</body>
</html>
-
BrowserProperties 浏览器相关的配置项
-
ValidateCodeProperties 验证码相关的
-
OAuth2Properties OAuth配置
-
SocialProperties Social配置
-
由于这些配置类是 app 和 browser 项目公用的,写在core里面
@ConfigurationProperties( prefix = "playmaker.security")//读取配置中配置名为playmaker.security的项目项
public class SecurityProperties {
private BrowserProperties browser = new BrowserProperties();
public BrowserProperties getBrowser() {
return browser;
}
public void setBrowser(BrowserProperties browser) {
this.browser = browser;
}
}
public class BrowserProperties {
private String loginPage = "/playmaker-signIn.html";//默认配置
public String getLoginPage() {
return loginPage;
}
public void setLoginPage(String loginPage) {
this.loginPage = loginPage;
}
}
- 为了职责分离,单独写一个入口被扫描开启的配置类
@Configuration
@EnableConfigurationProperties(SecurityProperties.class)//EnableConfigurationProperties 的作用是标明加载哪一个类 这效果和直接在目标类上写上@Configuration效果一样
public class SecurityCoreConfig {
}
- 不要忘记放行了
@RestController
public class BorowserSecurityController {
private Logger logger = LoggerFactory.getLogger(getClass());
private RequestCache requestCache = new HttpSessionRequestCache();//跳转之前spring security用HttpSessionRequestCache这个类把当前请求缓存到这个session里面去 所以当我们跳转到authentication/require这个请求时我们可以用这个类,从session中把之前缓存的请求拿出来
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();//spring中的跳转工具
@Autowired
private SecurityProperties securityProperties;//注入
@RequestMapping("/authentication/require")
@ResponseStatus(code= HttpStatus.UNAUTHORIZED) //401
public SimpleResponse authenticationrequire(HttpServletRequest request, HttpServletResponse response) throws IOException {
SavedRequest savedRequest = requestCache.getRequest(request,response);//SavedRequest就是之前缓存的请求
if(savedRequest!=null){
String redirectUrl = savedRequest.getRedirectUrl();
logger.info("跳转的地址:"+redirectUrl);
if(StringUtils.endsWithIgnoreCase(redirectUrl,".html")){
redirectStrategy.sendRedirect(request,response,securityProperties.getBrowser().getLoginPage());//注入url
}
}
return new SimpleResponse("访问的服务需要身份认证,请引导用户到登录页");
}
}
@Autowired
private SecurityProperties securityProperties;//注入
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()//form表单 高版本中默认是表单 低版本中默认是HttpBasic
.loginPage("/authentication/require") //指定登录页面的url
.loginProcessingUrl("/authentication/form") //指定处理登录请求的url
//httpBasic() //httpBasic
.and()
//对请求授权配置
.authorizeRequests()
.antMatchers("/playmaker-signIn.html",
"/authentication/require",
securityProperties.getBrowser().getLoginPage()).permitAll()//当访问这个url时不需要身份认证
.anyRequest()//对任意请求都必须是已认证才能访问
.authenticated()
.and()
.csrf().disable();//关闭csrf
}
- 运行效果如下
- 访问index.html(因为你自定义登录页面)
- 没有自定义,访问index.html跳转到标准登陆页面