SpringSecurity登录的密码加密和设置自动登录功能
环境前提搭建
- SpringSecurity连接数据库实现用户角色登录验证
- 数据库中的密码修改成加密过的密码,可以用下面的测试类转换
@Test
void contextLoads() {
BCryptPasswordEncoder bCryptPasswordEncoder= new BCryptPasswordEncoder();
System.out.println(bCryptPasswordEncoder.encode("你的密码"));
}
具体实现
登录的login.html界面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form th:action="@{/securitylogin}" method="post">
用户名:<input type="text" name="username">
<br>
密码:<input type="password" name="password">
<br>
<input type="checkbox" name="remember-me" value="1">自动登录
<!--通过阅读源码我们可以知道,name的名默认是remember-me也可以在SpringSecurity的配置文件里面改,但是value可以是:1,true,on和yes这4种-->
<input type="submit" value="登陆">
</form>
</body>
</html>
SpringSecurity的配置类
package com.pning.studysecurity.config;
import com.pning.studysecurity.service.ISystemUser;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import javax.annotation.Resource;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
ISystemUser iSystemUser;
//授权
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/toLogin").permitAll()
.antMatchers("/leave_1/**").hasRole("vip1")
.antMatchers("/leave_2/**").hasRole("vip2")
.antMatchers("/leave_3/**").hasRole("vip3");
http.formLogin().loginPage("/toLogin").loginProcessingUrl("/securitylogin");
http.logout().deleteCookies("remove").invalidateHttpSession(true).logoutSuccessUrl("/toLogin");
http.rememberMe().tokenValiditySeconds(60);//开启自动登录功能,并且设置过期时间为60秒
http.csrf().disable();//关闭csrf,不然会拦截没有token的put,post,delete等非查询的方法
}
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(iSystemUser)
.passwordEncoder(new BCryptPasswordEncoder());//添加加密类型
}
}
通过用户名查询密码和角色的接口
在之前继承了UserDetailsService的接口的实现类中吧{noop}
删除,表名我们已经对密码加了密
package com.pning.studysecurity.service.imp;
import com.pning.studysecurity.mapper.SystemUserMapper;
import com.pning.studysecurity.pojo.SystemUser;
import com.pning.studysecurity.service.ISystemUser;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
@Service
public class SystemUserImp implements ISystemUser {
@Resource
SystemUserMapper systemUserMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SystemUser systemUser = systemUserMapper.selectSystemUserByUserName(username);
if(systemUser!=null){
List<SimpleGrantedAuthority> roles = new ArrayList<>();
for (String roleName:systemUser.getRoles()){
roles.add(new SimpleGrantedAuthority(roleName));
}
return new User(systemUser.getUsername(),systemUser.getPassworld(),roles);//取消{noop}的不加密形式
}
return null;
}
}
验证
remember-me功能的token进行持久化
目前为止就完成了简单的自动登录功能,但是有个问题,就是现在的这个是cookie存放在用户浏览起这边的,如果在这个cookie未过期的时间中,有人把这个remember-me的cookie放在自己浏览器也是可以直接登陆的,而且这个cookie里面还有一些重要的信息,哪怕是加密过也是不安全的。
SpringSecurity为此提供了一个机制,就是在remember-me的cookie中设置一个没有意义的加密字符串,并且这个字符串存在数据库中,自动登录之前将这个字符串和数据库的字符串进行对比,两者一致才能成功自动登录。
注意的是:存储这个用于验证的字符串的数据库表必须是官方提供的那张表,字段什么都不能修改,如果修改的话将无法使用
CREATE TABLE persistent_logins (
username VARCHAR(64) NOT NULL,
series VARCHAR(64) PRIMARY KEY,
token VARCHAR(64) NOT NULL,
last_used TIMESTAMP NOT NULL);
在SpringSecurity中配置
需要添加数据源和注入持久化token的组件
package com.pning.studysecurity.config;
import com.pning.studysecurity.service.ISystemUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.annotation.Resource;
import javax.sql.DataSource;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
ISystemUser iSystemUser;
@Autowired
private DataSource dataSource;//配置数据源
//授权
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/toLogin").permitAll()
.antMatchers("/leave_1/**").hasRole("vip1")
.antMatchers("/leave_2/**").hasRole("vip2")
.antMatchers("/leave_3/**").hasRole("vip3");
http.formLogin().loginPage("/toLogin").loginProcessingUrl("/securitylogin");
http.logout().deleteCookies("remove").invalidateHttpSession(true).logoutSuccessUrl("/toLogin");
http.rememberMe()
.tokenValiditySeconds(60)
.tokenRepository(persistentTokenRepository());//配置数据源
http.csrf().disable();
}
//注入持久化token的组件
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
return jdbcTokenRepository;
}
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(iSystemUser)
.passwordEncoder(new BCryptPasswordEncoder());//添加加密类型
}
}
验证