实现原理
- 认证成功后通过cookie向浏览器中存入加密字符串,并且向数据库中存入加密字符串和用户信息字符串。
- 再次访问获取cookie信息,使用加密字符串到数据库中进行比对,如果查询到对应的信息,认证成功,可以登录。
spring security底层实现
具体实现
创建数据表
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;
然后,在配置类中注入数据源,配置操作数据库对象,并且配置自动登录。
SecurityConfig.java
package com.kgf.security.config;
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.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.sql.DataSource;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
//注入数据源
@Autowired
private DataSource dataSource;
//配置操作数据库对象
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
//jdbcTokenRepository.setCreateTableOnStartup(true); 启动时创建persistent_logins表
return jdbcTokenRepository;
}
/***
* 设置登录的用户
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//这是使用内存的方式
// BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
// String password = encoder.encode("123456");
// auth.inMemoryAuthentication().withUser("test").password(password).roles("admin");
//下面使用自定义接口实现类从数据库查询
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login.html")//登录页面设置
.loginProcessingUrl("/user/login")//登录访问路径,这个不需要自己写controller,配置即可,这个是spring security自动完成
.defaultSuccessUrl("/success.html").permitAll()//登录成功之后跳转的路径
.and().authorizeRequests()
.antMatchers("/","/test/hello","/user/login").permitAll()//设置这些路径可以直接访问,不需要认证
//1、hasAuthority方法
//.antMatchers("/test/index").hasAuthority("admin")//需要用户具有admin角色权限才行
//2、hasAnyAuthority方法
.antMatchers("/test/index").hasAnyAuthority("admin,manager")//需要用户具有admin角色权限才行
.anyRequest().authenticated()
//
.and().rememberMe().tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(3600) //设置有效时长,单位秒
.userDetailsService(userDetailsService) //设置userDetailsService底层来操作数据库
.and().exceptionHandling().accessDeniedPage("/unauth.html")//配置没有权限访问跳转自定义页面,必须配置在这里,配置在上面不起作用
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/test/hello").permitAll()//退出
.and().csrf().disable();//关闭csrf防护
}
/***
* 注入PasswordEncoder到spring中
* @return
*/
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
最后在登录页面添加记住我复选框:
<!DOCTYPE html>
<!-- 需要添加
<html xmlns:th="http://www.thymeleaf.org">
这样在后面的th标签就不会报错
-->
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>xx</title>
</head>
<body>
<h1>表单提交</h1>
<!-- 表单提交用户信息,注意字段的设置,直接是*{} -->
<form action="/user/login" method="post">
<input type="text" name="username" />
<input type="text" name="password" />
<input type="checkbox" name="remember-me" title="记住我"/>自动登录
<input type="submit" />
</form>
</body>
</html>
注意:自动登录的name必须为remember-me
测试:
点击登录后查询返回的cookie
我们在访问接口: