创建一个springboot项目
springboot版本依赖 使用 2.3.12.RELEASE
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
</dependencies>
用户表
DROP TABLE IF EXISTS `t_admin`;
CREATE TABLE `t_admin` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`create_time` bigint(32) NOT NULL COMMENT '创建时间',
`enabled` tinyint(2) NOT NULL DEFAULT 0 COMMENT '是否启用: 0 不启动,1 启用',
`name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '姓名',
`password` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码',
`update_time` bigint(32) NOT NULL COMMENT '更新时间',
`username` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = 'Admin对象 用户表' ROW_FORMAT = Dynamic;
配置文件
spring:
datasource:
name:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://:3310/?characterEncoding=utf8&useSSL=false
username: root
password: 密码
创建用户类
public class Admin implements Serializable, UserDetails {
private static final long serialVersionUID = 1L;
private Long id;
private Long createTime;
private boolean enabled;
private String name;
private String password;
private Long updateTime;
private String username;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getCreateTime() {
return createTime;
}
public void setCreateTime(Long createTime) {
this.createTime = createTime;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setPassword(String password) {
this.password = password;
}
public Long getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Long updateTime) {
this.updateTime = updateTime;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "Admin{" +
"id=" + id +
", createTime=" + createTime +
", enabled=" + enabled +
", name='" + name + '\'' +
", password='" + password + '\'' +
", updateTime=" + updateTime +
", username='" + username + '\'' +
'}';
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return this.enabled;
}
}
新建mapper
public interface AdminMapper extends BaseMapper<Admin> {
@Select("select * from t_admin " +
"${ew.customSqlSegment}")
Admin getAllAdmins(@Param(Constants.WRAPPER) Wrapper wrapper);
}
service
public interface IAdminService extends IService<Admin> {
Admin getAllAdmins(String name);
}
@Service
public class AdminServiceImpl extends ServiceImpl<AdminMapper, Admin> implements IAdminService {
@Resource
private AdminMapper adminMapper;
public Admin getAllAdmins(String name) {
System.out.println(name);
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("name",name);
return adminMapper.getAllAdmins(queryWrapper);
}
}
新建UserServiceImpl 实现UserDetailsService
这里实现通过查询数据库返回密码到密码验证类
@Service
public class UserServiceImpl implements UserDetailsService {
@Autowired
private AdminServiceImpl adminService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//数据库查询
Admin allAdmins = adminService.getAllAdmins(username);
//1.根据username查询数据库
if(allAdmins==null){
throw new UsernameNotFoundException("用户名或密码错误");
}
return new User(allAdmins.getName(), allAdmins.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
}
}
新建config需要两个配置类
密码校验UserAuthenticationProvider
把上一个UserDetailsService 类注入进来
重点是这个 方法 bCryptPasswordEncoder.matches(password, userDetails.getPassword());
/**
* 完成校验工作
*/
@Component
public class UserAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserDetailsService userDetailService;
/**
* 进行身份认证
*
* @param authentication
* @return
* @throws AuthenticationException
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
System.out.println("进行身份认证");
// 获取用户输入的用户名和密码
String username = authentication.getName();
String password = authentication.getCredentials().toString();
System.out.println(username);
// 获取封装用户信息的对象
UserDetails userDetails = userDetailService.loadUserByUsername(username);
System.out.println(" =========== 权限:"+userDetails.getAuthorities());
// 进行密码的比对
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
boolean flag = bCryptPasswordEncoder.matches(password, userDetails.getPassword());
// 校验通过
if (flag){
// 将权限信息也封装进去
return new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal,rzk"));
}
// 验证失败返回 null
return null;
}
/**
* 这个方法 确保返回 true 即可,
*
* @param aClass
* @return
*/
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
SecurityConfig
/**
* @PackageName : com.rzk.config
* @FileName : SecurityConfig
* @Description :
* @Author : rzk
* @CreateTime : 13/12/2021 下午10:19
* @Version : v1.0
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("执行自定义登录逻辑方法");
//表单登录
http.formLogin()
//自定义登录页面
.loginPage("/login.html")
//自定义登录逻辑
.loginProcessingUrl("/login")
//登录成功后跳转的页面,必须是post
.successForwardUrl("/toMain")
//登录失败后跳转页面,必须是post
.failureForwardUrl("/toError");
//授权
http.authorizeRequests()
//放行页面
.antMatchers("/login.html").permitAll()
.antMatchers("/error.html").permitAll()
//所有请求都必须被认证(登录)
.anyRequest().authenticated();
//关闭csrf防护
http.csrf().disable();
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
controller
@Controller
public class LoginController {
@Autowired
private IAdminService adminService;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private UserDetailsService userDetailsService;
@RequestMapping("/login")
public String login(@RequestParam("username") String username, @RequestParam("password")String password){
Admin allAdmins = adminService.getAllAdmins(username);
//登录
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (userDetails == null || !passwordEncoder.matches(password,allAdmins.getPassword())){
System.out.println("空");
}
return "redirect:main.html";
}
@RequestMapping("/toMain")
public String toMain(){
return "redirect:main.html";
}
@RequestMapping("/toError")
public String error(){
return "redirect:error.html";
}
}
这里需要三个html页面
error.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
登录失败
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login" method="post">
用户名:<input type="text"name="username"/><br/>
密码:<input type="password"name="password"/><br/>
<input type="submit"value="登录"/><br/>
</form>
</body>
</html>
main.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录成功</h1>
</body>
</html>
还没登录如果访问登录成功页面 是访问不了的