本文来说下Spring Security:基于MySQL数据库的身份认证和角色授权 。本文为了上手简单,只用了一张user表。
文章目录
概述
需求缘起
在前面我们使用基于内存的方式体验了下Spring Security,在实际项目中,都是需要数据库进行操作的。
编码思路
本节使用MySQL数据库进行操作,需要添加MySQL数据库的驱动包以及配置好数据源和mybatis。
创建项目
创建一个 SpringBoot 模块项目,选择相关依赖:
先搭建项目正常访问,在pom.xml中,先把Spring Security依赖注释
创建一张用户表,并且在里面添加两条数据
在MySQL数据库中创建一张用户表,id主键自增,并添加两个用户如下:
create database Security;
use Security;
drop table if exists user;
create table user
(
id int auto_increment primary key comment '用户ID',
username varchar(20) comment '用户名字',
password varchar(100) comment '用户密码(加密后的)',
role varchar(10) comment '用户角色(可以把这个抽出来,因为一个用户可能不止一个角色)'
);
insert into user (username, password, role)
VALUES ('root', '123456', 'root'),
('admin', '123456', 'admin');
创建pojo实体类
创建一个创建pojo实体类
package cn.wideth.entity.security;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo {
private int id;
private String username;
private String password;
private String role;
}
创建UserMapper接口
package cn.wideth.mapper;
import cn.wideth.entity.security.UserInfo;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface UserMapper {
//通过用户名查询用户
UserInfo getUserByName(@Param("username") String username);
}
创建UserMapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.wideth.mapper.UserMapper">
<select id="getUserByName" resultType="cn.wideth.entity.security.UserInfo">
select * from user where username = #{username}
</select>
</mapper>
创建IUserService
package cn.wideth.service;
import cn.wideth.entity.security.UserInfo;
public interface IUserService {
UserInfo getUserByName(String username);
}
创建UserServiceImpl
package cn.wideth.service.impl;
import cn.wideth.entity.security.UserInfo;
import cn.wideth.mapper.UserMapper;
import cn.wideth.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
@Override
public UserInfo getUserByName(String username) {
return userMapper.getUserByName(username);
}
}
配置application.yml文件
server:
port: 9090
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/security?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8
username: root
password: we18256987759
# redis:
# host: localhost
# port: 6379
profiles:
include: config
mybatis:
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
user:
name: admin
编写UserController
package cn.wideth.controller;
import cn.wideth.entity.security.UserInfo;
import cn.wideth.service.IUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/user")
@Api(description = "权限测试", tags = "权限测试")
public class UserController {
@Autowired
private IUserService iUserService;
@GetMapping("/getUser")
@ApiOperation(value = "用户权限测试接口", notes = "用户权限测试接口")
public UserInfo getUser(@RequestParam String username) {
return iUserService.getUserByName(username);
}
}
启动项目,进行测试
访问http://localhost:9090/api/user/getUser?username=root
基于数据库的身份认证
把pom.xml中的Spring Security依赖注释去掉
出现默认的登录页面
自定义LoginUserDetailsService
自定义一个UserDetailsService,取名为LoginUserDetailService,该类需要实现接口UserDetailsService,主要是实现loadUserByUsername方法:
package cn.wideth.config;
import cn.wideth.entity.security.UserInfo;
import cn.wideth.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
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.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/***
* 该类需要实现接口UserDetailsService,
* 主要是实现loadUserByUsername方法:
* 用来加载用户保存在数据库中的登录信息
*/
@Component
@Slf4j
public class LoginUserDetailService implements UserDetailsService {
@Autowired
private IUserService iUserService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("用户的登录信息被加载了");
//通过username获取用户信息
UserInfo userInfo = iUserService.getUserByName(username);
log.info("数据库中保存的用户信息" + userInfo);
if(userInfo == null) {
throw new UsernameNotFoundException("not found");
}
//定义权限列表.
List<GrantedAuthority> authorities = new ArrayList<>();
// 用户可以访问的资源名称(或者说用户所拥有的权限) 注意:必须"ROLE_"开头
authorities.add(new SimpleGrantedAuthority("ROLE_"+userInfo.getRole()));
//注意这里的user为security内置的用户类型,类型为org.springframework.security.core.userdetails.User
User userDetails = new User(userInfo.getUsername(),passwordEncoder.encode(userInfo.getPassword()),authorities);
log.info(userDetails.toString());
return userDetails;
}
}
说明:
(1) 通过username获取用户的信息。
(2) 定义一个User(实现了接口UserDetails)对象,返回用户的username,passowrd和权限列表。
(3) 需要注意,定义角色集的时候,需要添加前缀“ROLE_”。
(4) 这里的密码需要使用PasswordEncoder进行加密,否则会报“无效的凭证”。
配置SecurityConfig
package cn.wideth.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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* Security配置
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private LoginUserDetailService loginUserDetailService;
/**
* 强散列哈希加密实现
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 身份认证接口
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 从数据库读取的用户进行身份认证
auth.userDetailsService(loginUserDetailService).passwordEncoder(passwordEncoder());
}
}
启动项目,进行测试
输入数据库中不存在的用户名或者密码,无法登录
输入正确的用户名和密码,可以登录。
本文小结
本文简单介绍了Spring Security,基于MySQL数据库的身份认证,后面会在此基础之上进行详细的后续知识介绍。