SpringSecurity系列,第二章:RememberMe 和 异常处理
一、RememberMe
RememberMe这个功能,是为了方便用户在下次登录时直接登录。避免再次输入 用户名 和 密码。
下面我们记录下如何使用RememberMe这个功能的。
1、修改login.html页面
添加remember-me,注意:字段的name必须是remember-me
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<h1>登陆</h1>
<form method="post" action="/login">
<div>
用户名:<input type="text" name="username">
</div>
<div>
密码:<input type="password" name="password">
</div>
<div>
<label><input type="checkbox" name="remember-me">记住密码</label>
</div>
<div>
<button type="submit">登陆</button>
<button type="reset">重置</button>
</div>
</form>
</body>
</html>
2、两种存储登录信息方式
2.1、Cookie存储
这种方式只需在配置类中的Configure()方法添加rememberMe()即可
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html") //用户未登录时,访问资源都跳转到该页面,登录页面
.loginProcessingUrl("/login")//登录表单,form中的action地址,也就是处理认证请求的路径
.usernameParameter("username")//登录表单,form中用户名input框的name名,默认是username
.passwordParameter("password")//登录表单,form中密码input框的password名,默认是password
.defaultSuccessUrl("/")//登录成功后,默认跳转的路径
.permitAll()
.and()
.logout().permitAll()
.and()
.rememberMe();//记住账号和密码
//关闭csrf跨域
http.csrf().disable();
}
当我们登录勾选remember-me后,会自动在Cookie中保存一个为remember-me的cookie。
默认有效期为2周,其值是一个加密字符串
2.2、数据库存储
使用COOKIE存储用户信息固然很方便,但是COOKIE毕竟是保存在客户端的,而且cookie的值还与用户名、密码这些敏感数据相关,虽然加密了,但是将其存在客户端,毕竟不太安全。
Spring Security还提供了另一种相对安全的机制:在客户端的cookie中,进保存一个无意义的加密串(与用户名、密码等无关),然后在数据库中保存该加密串 与 用户信息的 对应关系,自动登录时,用cookie中的加密串,到数据库中验证,如果通过则自动登录。
原理:
浏览器发起表单登录请求时,当通过UsernamePasswordAuthenticationFilter认证成功后,会经过RememberMeService,在其中有一个TokenRepository,它会生成一个token,首先将token写入到浏览器的cookie中,然后将token、认证成功的用户名写入数据库。
在浏览器进行下次请求时,会经过RememberMeAuthenticationFilter,它会读取cookie中的token,交给RememberMeService从数据库中查询记录。如果存在记录,会读取用户名并去调取UserDetailsService,获取用户信息,并将用户信息放到Spring Security中,实现自动登录。
RememberMeAuthenticationFilter在整个SpringSecurity的过滤链中是比较靠后的,也就是说在传统的登录方式都无法通过后才会使用自动登录。
2.2.1、创建表存储Token
DROP TABLE IF EXISTS `persistent_logins`;
CREATE TABLE `persistent_logins` (
`username` varchar(64) NOT NULL,
`series` varchar(64) NOT NULL,
`token` varchar(64) NOT NULL,
`last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`series`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2.2.2、在配置文件中注入PersistentTokenRepository
在WebSecurityConfig配置类中添加如下:
@Autowired
private DataSource dataSource;
@Bean
public PersistentTokenRepository persistentTokenRepository(){
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);
return tokenRepository;
}
Configure中配置自动登录
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html") //用户未登录时,访问资源都跳转到该页面,登录页面
.loginProcessingUrl("/login")//登录表单,form中的action地址,也就是处理认证请求的路径
.usernameParameter("username")//登录表单,form中用户名input框的name名,默认是username
.passwordParameter("password")//登录表单,form中密码input框的password名,默认是password
.defaultSuccessUrl("/")//登录成功后,默认跳转的路径
.permitAll()
.and()
.logout().permitAll()
.and()
.rememberMe()//记住账号和密码
.tokenRepository(persistentTokenRepository());//tokenRepositoryBean 去数据库中查找RememberMe信息
//关闭csrf跨域
http.csrf().disable();
}
二、异常处理
当我们登录失败是,Spring Security会帮我们跳转到 http://127.0.0.1:8081/login.html?error 。这是Spring Security默认的失败URL。如果我们不手动处理这个异常,这个异常是不会被处理的。
1、常见的异常
一下的异常都是AuthenticationException的子类
UsernameNotFoundException 用户不存在
DisabledException 用户被禁用
BadCredentialsException 坏的凭据
LockedException 账户被锁定
AccountExpireException 账户过期
CredentialsExpiredException凭据过期
...
2、异常处理内部流程
(1)在AbstractAuthenticationProcessingFilter类的doFilter方法中,我们发现,出现异常后,它会调用unsuccessfulAuthentication()方法
(2)在unsuccessfulAuthentication()方法中,它将异常交给了SimpleUrlAuthenticationFailureHandler类的onAuthenticationFailure()方法处理
(3)在onAuthenticationFailure()方法中,首先会判断defaultFailureUrl是否存在,
若果不存在,直接返回401 Unauthorized
如果存在,先存储异常saveException,然后判断forwardToDestination ,是否服务器跳转,默认为false,直接重定向到defaultFailureUrl
saveException,则默认将Exception存储到Session中
3、处理异常
由上分析可知,我们需要配置一下defaultFailureUrl。只要指定错误的URL即可。
(1)指定错误URL,在WebSecurityConfig中添加failureUrl
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html") //用户未登录时,访问资源都跳转到该页面,登录页面
.loginProcessingUrl("/login")//登录表单,form中的action地址,也就是处理认证请求的路径
.usernameParameter("username")//登录表单,form中用户名input框的name名,默认是username
.passwordParameter("password")//登录表单,form中密码input框的password名,默认是password
.defaultSuccessUrl("/")//登录成功后,默认跳转的路径
.failureUrl("/login/error")//登录失败URL
.permitAll()
.and()
.logout().permitAll()
.and()
.rememberMe()//记住账号和密码
.tokenRepository(persistentTokenRepository());//tokenRepositoryBean 去数据库中查找RememberMe信息
//关闭csrf跨域
http.csrf().disable();
}
(2)Controller中处理异常
@ResponseBody
@GetMapping("/login/error")
public String loginError(HttpServletRequest req, HttpServletResponse resp){
AuthenticationException excp =
(AuthenticationException) req.getSession().getAttribute("SPRING_SECURITY_LAST_EXCEPTION");
return "异常信息为:"+excp.getMessage();
}
标签:password,http,04,登录,默认,SpringSecurity,RememberMe,login,表单
From: https://blog.csdn.net/pilot_speed/article/details/144454932