个性化用户认证流程1
前面使用spring security默认的认证流程。处理了自定义的用户数据,密码加密;
但是在实际开发中,肯定是要使用自己开发的页面、登录成功失败的业务处理等。
本节内容
* 自定义登录页
* 自定义登录成功处理
* 自定义登录失败处理
自定义登录页面
在cn.mrcode.imooc.springsecurity.securitybrowser.BrowserSecurityConfig中修改配置
http
// 定义表单登录 - 身份认证的方式
.formLogin()
.loginPage("/imocc-signIn.html")
.and()
.authorizeRequests()
// 放行这个路径
.antMatchers("/imocc-signIn.html").permitAll()
如果不对该静态文件放行的话,将会无限跳转,造成错误:localhost 将您重定向的次数过多。
对于html存放资源文件夹下的哪一个目录,这个有点懵逼:
最后放在static下面才能访问到
|- resources
|-static
|-imocc-signIn.html
html内容如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>标准登录页面</title>
</head>
<body>
<h2>标准登录页面</h2>
<h3>表单登录</h3>
<form action="/authentication/form" method="post">
| 用户名:||
| :-----: | :-----: |
| 用户名: | |
| 密码: | |
| 登录 |
</form>
</body>
</html>
注意这里的路径:acrion="/authentication/form"
;路径是自定义的,
而UsernamePasswordAuthenticationFilter默认是处理/login
路径的登录请求
public UsernamePasswordAuthenticationFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}
.formLogin()
.loginPage("/imocc-signIn.html")
// 处理登录请求路径
.loginProcessingUrl("/authentication/form")
.and()
.authorizeRequests()
.antMatchers("/imocc-signIn.html").permitAll()
我的天,以后一定要注意看方法名好吗?英文不好就是这么容易被看懵逼。不知道怎么弄
再次测试,没有出现和视频中一样的出现_csrf的错误情况,而是又跳回来登录页面了;
但是把scrf关闭之后,就正常了;最后这个完整的配置如下
http
.formLogin()
.loginPage("/imocc-signIn.html")
.loginProcessingUrl("/authentication/form")
.and()
.authorizeRequests()
.antMatchers("/imocc-signIn.html").permitAll()
.anyRequest()
.authenticated()
.and()
.csrf().disable(); // csrf 在后面章节会讲解
这里有一个疑问的问题:
loginProcessingUrl("/authentication/form")
会认为必须是跳转到我们自己写的这个真是存在的控制器里面,
实际上这个路径对应的控制器不存在也没有关系。因为不会走。
这里看起来更像是对security默认/login路径的重命名;
这里虽然配置了自定义的路径,但是都统一跳转到了静态页面,
那么要怎么实现 根据请求来分发是返回html内容?还是返回json内容呢?
在前后分离的情况下,都用ajax来请求,肯定不能返回html咯;
处理不同类型的请求
思路是上面图中这样。那么很简单只要把登录地址换成自定义的就好了。
@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// 有三个configure的方法,这里使用http参数的
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
// .loginPage("/imocc-signIn.html")
// 更换成自定义的一个真实存在的处理器地址
.loginPage("/authentication/require")
.loginProcessingUrl("/authentication/form")
.and()
.authorizeRequests()
// 放行这个路径
.antMatchers("/imocc-signIn.html", "/authentication/require").permitAll()
.anyRequest()
.authenticated()
.and()
.csrf().disable()
;
}
}
编写处理请求的处理器
package cn.mrcode.imooc.springsecurity.securitybrowser;
import cn.mrcode.imooc.springsecurity.securitybrowser.support.SimpleResponse;
import org.apache.catalina.servlet4preview.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.HttpServletResponse;
import java.io.IOException;
/**
* ${desc}
* @author zhuqiang
* @version 1.0.1 2018/8/3 14:58
* @date 2018/8/3 14:58
* @since 1.0
*/
@RestController
public class BrowserSecurityController {
Logger logger = LoggerFactory.getLogger(getClass());
// 封装了引发跳转请求的工具类,看实现类应该是从session中获取的
private RequestCache requestCache = new HttpSessionRequestCache();
// spring的工具类:封装了所有跳转行为策略类
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
/**
* 当需要身份认证时跳转到这里
* @param request
* @param response
* @return
*/
@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();
logger.info("引发跳转的请求:" + targetUrl);
// 如果是html请求,则跳转到登录页
if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) {
redirectStrategy.sendRedirect(request, response, "/imocc-signIn.html");
}
}
// 否则都返回需要认证的json串
return new SimpleResponse("访问的服务需要身份认证,请引导用户到登录页");
}
}
SimpleResponse 类只是一个返回结果的信息类
package cn.mrcode.imooc.springsecurity.securitybrowser.support;
public class SimpleResponse {
private Object content;
public SimpleResponse(Object content) {
this.content = content;
}
public Object getContent() {
return content;
}
public void setContent(Object content) {
this.content = content;
}
}
我们的目的是写一个可重用的安全模块,需要把一些配置(比如这里的html静态页面的配置)让使用方来配置
思路是使用配置文件:
在security-demo/application.yml中;
注意: 这里我才看明白视频中被依赖的security-browser项目没有写启动类,也没有自己的application.yml配置文件;
应该就是只写一个可重用的模块
imooc:
security:
browser:
loginPage: /demo-signIn.html
该模块大概会提供20多项配置,所以封装成配置类,分为不同的功能,结构如下:
- SecurityProperties 项目提供的配置类
- BrowserProperties
- ValidateCodeProperties
- Oath2Properties
- SocialProperties
core 配置类的编写
由于这些配置类是 app 和 browser 项目公用的,写在core里面
package cn.mrcode.imooc.springsecurity.securitycore.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* ${desc}
* @author zhuqiang
* @version 1.0.1 2018/8/3 15:28
* @date 2018/8/3 15:28
* @since 1.0
*/
@ConfigurationProperties(prefix = "imooc.security")
public class SecurityProperties {
/** imooc.security.browser 路径下的配置会被映射到该配置类中 */
private BrowserProperties browser = new BrowserProperties();
public BrowserProperties getBrowser() {
return browser;
}
public void setBrowser(BrowserProperties browser) {
this.browser = browser;
}
}
package cn.mrcode.imooc.springsecurity.securitycore.properties;
public class BrowserProperties {
/** 登录页面路径 */
private String loginPage = "/imocc-signIn.html";
public String getLoginPage() {
return loginPage;
}
public void setLoginPage(String loginPage) {
this.loginPage = loginPage;
}
}
为了职责分离,单独写一个入口被扫描开启的配置类
package cn.mrcode.imooc.springsecurity.securitycore;
import cn.mrcode.imooc.springsecurity.securitycore.properties.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* 让SecurityProperties这个配置类生效
* EnableConfigurationProperties 的作用是标明加载哪一个类
* 这效果和直接在目标类上写上@Configuration效果一样
*/
@Configuration
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityCoreConfig {
}
使用配置类重构brower
之前写死 登录页面 相关代码处都需要更改从配置类中获取,使用 Autowired 注解可以获得SecurityProperties配置类实例
@Autowired
private SecurityProperties securityProperties;
// 别忘记了拦截放行的地方也需要更改为配置类的属性
.antMatchers("/authentication/require",
securityProperties.getBrowser().getLoginPage()).permitAll()