Spring Security学习(二)——使用数据库保存密码

前言

上一篇文章我们已经快速搭起Spring Security应用。我们使用的是默认的用户名密码。通常项目中我们使用数据库保存密码,并且数据库的密码是加密保存的。如何做到呢,是这篇文章的重点。

引入数据库

新建数据库表

我们新建数据库“spring_security_test”,然后再里面新建一个简单的用户表,这个表非常简单,只有用户名和密码字段:

CREATE TABLE `sys_user`  (
  `id` int(11) NOT NULL,
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;

引入mybatis-plus

为了方便数据库操作,这里使用mybatis-plus。当然如果读者喜欢使用mybatis或者单纯使用jdbc连接也可以,自行实现即可。

首先引入pom依赖:

<dependency>
	<groupId>com.baomidou</groupId>
	<artifactId>mybatis-plus-boot-starter</artifactId>
	<version>3.4.3</version>
</dependency>
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>8.0.16</version>
</dependency>
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid-spring-boot-starter</artifactId>
	<version>1.1.10</version>
</dependency>

接着配置mybatis-plus的配置项,在application.yml中添加配置:

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    druid:
        url: jdbc:mysql://127.0.0.1:3306/spring_security_test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: 

创建用户表实体SysUserEntity:

package com.sadoshi.springsecurity.domain.entity;

import com.baomidou.mybatisplus.annotation.TableName;

import lombok.Data;

@Data
@TableName("sys_user")
public class SysUserEntity {

	private Long id;
	private String username;
	private String password;
}

新建DAO接口SysUserMapper:

package com.sadoshi.springsecurity.dao;

import org.apache.ibatis.annotations.Mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sadoshi.springsecurity.domain.entity.SysUserEntity;

@Mapper
public interface SysUserMapper extends BaseMapper<SysUserEntity>{

}

新建Service层接口SysUserService和其实现类SysUserServiceImpl:

package com.sadoshi.springsecurity.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.sadoshi.springsecurity.domain.entity.SysUserEntity;

public interface SysUserService extends IService<SysUserEntity>{

}
package com.sadoshi.springsecurity.service.impl;

import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sadoshi.springsecurity.dao.SysUserMapper;
import com.sadoshi.springsecurity.domain.entity.SysUserEntity;
import com.sadoshi.springsecurity.service.SysUserService;

@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUserEntity> implements SysUserService{

}

至此引入完毕。

实现Spring Security读取用户名密码接口

前面我们已经准备好了用户名密码表,也实现了相应的数据库接口,接下来要做的事就是让Spring Security读取用户名密码,让其在内部比对用户名密码是否匹配。

实现UserDetails接口

UserDetails是Spring Security用户接口类,我们要实现这个接口类,用于返回各类用户名、密码、权限等。可以理解为这就是Spring Security内部的用户信息实体。创建LoginUser类实现该接口:

package com.sadoshi.springsecurity.domain.model;

import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.sadoshi.springsecurity.domain.entity.SysUserEntity;

public class LoginUser implements UserDetails{

	private SysUserEntity user;
	
	public LoginUser(SysUserEntity user) {
		this.user = user;
	}
	
	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		return null;
	}

	@Override
	public String getPassword() {
		return user.getPassword();
	}

	@Override
	public String getUsername() {
		return user.getUsername();
	}

	@Override
	public boolean isAccountNonExpired() {
		return true;
	}

	@Override
	public boolean isAccountNonLocked() {
		return true;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}

	@Override
	public boolean isEnabled() {
		return true;
	}
}

由于权限、用户是否锁定等信息目前我们还用不上,因此先简单配置,以确保我们测试正常。

实现UserDetailsService接口

我们还要实现一个接口,当Spring Security需要进行比对的时候,如何读取相关的用户信息,并返回一个用户信息的实体类给Spring Security,需要在这个接口中实现。我们新建UserDetailsServiceImpl类实现UserDetailsService接口:

package com.sadoshi.springsecurity.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sadoshi.springsecurity.domain.entity.SysUserEntity;
import com.sadoshi.springsecurity.domain.model.LoginUser;
import com.sadoshi.springsecurity.service.SysUserService;

@Component
public class UserDetailsServiceImpl implements UserDetailsService{

	@Autowired
	private SysUserService userService;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		QueryWrapper<SysUserEntity> queryWrapper = new QueryWrapper<SysUserEntity>();
		queryWrapper.eq("username", username);
		queryWrapper.last("limit 1");
		SysUserEntity user = userService.getOne(queryWrapper);
		if(user == null) {
			throw new RuntimeException("No user named " + username);
		}
		return (new LoginUser(user));
	}
}

查看Spring Security源码可知,Spring Security进行认证时会调用loadUserByUsername获取用户信息,因此我们实现这个方法,并返回我们LoginUser的实例。

验证

我们在数据库表sys_user新增一条记录:

 然后运行项目,并在浏览器输入http://localhost:8082/hello,跳转到登录页后,输入帐号jake,密码123。便可以登入。

密码的前缀

读者会注意到,密码项有个{noop}的前缀。前缀的作用是表示其加密的方式,{noop}表示没有加密。后面文章可以专门研究一下Spring Security的加密方式

小结

本文讲述如何使用数据库保存用户名密码,Spring Security如何读取这些信息。同时也展示了如何整合mybatis-plus到项目中。后面还提出了关于密码加密的疑问,留待后面文章学习。

猜你喜欢

转载自blog.csdn.net/sadoshi/article/details/127344716