在 上一节 我们完成了最基本的security配置,下面我们了解下如何配置用户信息,包括内存签名服务,数据库签名服务和自定义签名服务三种。
首先,说下内存签名服务,顾名思义,就是将用户的相关信息放在内存中,一般用于项目初期的环境搭建验证。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//用户信息加解密,单向不可逆
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
//在这里配置url访问权限
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().anyRequest().authenticated()
.and().formLogin();
}
/*
* 该方法是一个配置用户信息的方法,
* 使用内存签名服务,数据库签名服务和自定义签名服务
* 通过configure(HTTPSecurity httpSecurity)方法限定请求权限
* */
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//密码编码器
PasswordEncoder passwordEncoder = passwordEncoder();
//内存签名服务
auth.inMemoryAuthentication()
.passwordEncoder(passwordEncoder)
//注册用户admin,密码为123456,赋予用户和管理员的权限
.withUser("admin")
.password("$2a$10$6oba6BnY4Ux/ayNTX1KcOO6ZYYb17rnMmsL.bOg2wqU5UsfbLYfBO")
.roles("USER", "ADMIN")
.and()
//注册用户user,密码为123456
.withUser("user")
.password("$2a$10$6oba6BnY4Ux/ayNTX1KcOO6ZYYb17rnMmsL.bOg2wqU5UsfbLYfBO")
.roles("USER");
}
}
在spring 5的Security中都要求使用密码编码器,否则发生异常。inMemoryAuthentication()方法配置用户的信息,包括赋予角色。我们可以先写一个UserController页面和user页面看下效果。
UserController如下:
@Controller
public class UserController {
@GetMapping("/user")
public String user(){
return "user";
}
}
在src/resources/templates下新建一个user.heml。内存如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>welcome you, my lover!</h1>
</body>
</html>
然后,我们启动下试试效果,输入http://localhost:8080/ ,输入用户名和密码
接下来,就是通过访问数据库的信息来实现登录。
数据库主要有三个表,其中t_role表是角色表,就是说明有哪些角色(管理员,用户等);t_user表为用户表,包含用户的信息;t_user_role表为用户角色表(将角色表和用户表对应)。
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_role
-- ----------------------------
DROP TABLE IF EXISTS `t_role`;
CREATE TABLE `t_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role_name` varchar(60) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`note` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of t_role
-- ----------------------------
INSERT INTO `t_role` VALUES (1, 'ROLE_ADMIN', NULL);
INSERT INTO `t_role` VALUES (2, 'ROLE_USER', NULL);
-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` int(12) NOT NULL AUTO_INCREMENT,
`user_name` varchar(60) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`pwd` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`available` int(1) NULL DEFAULT 1 COMMENT '是否可用,1表示可用,0表示不可用',
`note` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `user_name`(`user_name`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of t_user
-- ----------------------------
INSERT INTO `t_user` VALUES (1, 'admin', '$2a$10$JqRPoogLhSHKlP5esSSJ2eMBQdyjeiOYRkl73EC2tD2Swuj1NpkCG', 1, 'ROLE_ADMIN,ROLE_USER');
INSERT INTO `t_user` VALUES (2, 'myuser', '$2a$10$JqRPoogLhSHKlP5esSSJ2eMBQdyjeiOYRkl73EC2tD2Swuj1NpkCG', 1, 'ROLE_USER');
-- ----------------------------
-- Table structure for t_user_role
-- ----------------------------
DROP TABLE IF EXISTS `t_user_role`;
CREATE TABLE `t_user_role` (
`id` int(12) NOT NULL AUTO_INCREMENT,
`role_id` int(12) NOT NULL,
`user_id` int(12) NOT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `role_id`(`role_id`, `user_id`) USING BTREE,
INDEX `FK_Reference_2`(`user_id`) USING BTREE,
CONSTRAINT `FK_Reference_1` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `FK_Reference_2` FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of t_user_role
-- ----------------------------
INSERT INTO `t_user_role` VALUES (1, 1, 1);
INSERT INTO `t_user_role` VALUES (2, 2, 1);
INSERT INTO `t_user_role` VALUES (3, 2, 2);
SET FOREIGN_KEY_CHECKS = 1;
在这里,更改下SecurityConfig类,
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//注入数据源
@Autowired
private DataSource dataSource = null;
//使用用户名查询密码
String pwdQuery = "select user_name, pwd, available from t_user where user_name =?";
//使用用户名查询角色信息
String roleQuery = "select u.user_name, r.role_name from t_user u, t_user_role ur, t_role r "
+ "where u.id = ur.user_id and r.id = ur.role_id and u.user_name =?";
//用户信息加解密,单向不可逆
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
//在这里配置url访问权限
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().anyRequest().authenticated()
.and().formLogin();
}
/*
* 该方法是一个配置用户信息的方法,
* 使用内存签名服务,数据库签名服务和自定义签名服务
* 通过configure(HTTPSecurity httpSecurity)方法限定请求权限
* */
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//密码编码器
PasswordEncoder passwordEncoder = passwordEncoder();
auth.jdbcAuthentication()
.passwordEncoder(passwordEncoder)
//数据源
.dataSource(dataSource)
//查询用户,自动判断密码是否一致
.usersByUsernameQuery(pwdQuery)
//赋予权限
.authoritiesByUsernameQuery(roleQuery);
}
}
现在就已经完成了。重新启动项目,输入http://localhost:8080/ 还是刚才的用户名和密码,效果如下:
接下来,我们来实现spring security的自定义认证。先修改下securityCOnfig类,
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsServiceImpl myUserDetailsService;
//用户信息加解密,单向不可逆
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
//在这里配置url访问权限,admin需要有admin授权,user则是有admin,user,一个角色即可访问
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests()
.antMatchers("/user").hasAnyRole("USER", "ADMIN")
.antMatchers("/admin").hasAuthority("ROLE_ADMIN")
.anyRequest().permitAll()
.and().formLogin()
//启动http基础验证
.and().httpBasic();
}
/*
* 该方法是一个配置用户信息的方法,
* 使用内存签名服务,数据库签名服务和自定义签名服务
* 通过configure(HTTPSecurity httpSecurity)方法限定请求权限
* */
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//密码编码器
PasswordEncoder passwordEncoder = passwordEncoder();
auth.userDetailsService(myUserDetailsService)
.passwordEncoder(passwordEncoder);
}
}
添加UserDO,和UserRoleDO
public class UserDO {
private Long id;
private String userName;
private String password;
private Integer available;
private String note;
/*省去get,set访问*/
}
----------------------------------------------
public class UserRoleDO extends UserDO {
private String roleName;
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
添加UserDao(类似UserMapper),在src/main/resource/mybatis/下添加UserMapper.xml
@Mapper
public interface UserDao {
List<UserDO> findAll();
List<UserRoleDO> findRolesByUserName(String userName);
UserDO getUserByName(String userName);
}
-------------------------------------------------------------
<?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="com.example.demo.dao.UserDao">
<select id="findAll" resultType="com.example.demo.domain.UserDO">
select * from t_user
</select>
<select id="findRolesByUserName" parameterType="String" resultType="com.example.demo.domain.UserRoleDO">
select a.*, b.role_name from t_user a, t_role b, t_user_role c
where a.user_name = #{userName} and a.id = c.user_id and b.id = c.role_id
</select>
<select id="getUserByName" resultType="com.example.demo.domain.UserDO" parameterType="String">
select * from t_user where user_name = #{userName}
</select>
</mapper>
然后添加UserService,UserServiceImpl
public interface UserService {
List<UserDO> findAll();
List<UserRoleDO> findRolesByUserName(String userName);
UserDO getUserByName(String userName);
}
---------------------------------------------------
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public List<UserDO> findAll() {
return userDao.findAll();
}
@Override
public List<UserRoleDO> findRolesByUserName(String userName) {
return userDao.findRolesByUserName(userName);
}
@Override
public UserDO getUserByName(String userName) {
return userDao.getUserByName(userName);
}
}
UserController类如下:
@Controller
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/hello")
public Object hello() {
return "hello";
}
@GetMapping("/user")
public String user() {
return "user";
}
@GetMapping("/admin")
public String admin() {
return "admin";
}
}
新建user.html, admin.html, hello.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>hello page</title>
</head>
<body>
<h1>你好,这里是hello 页面</h1>
<a href="/user">user</a>
<a href="/admin">admin</a>
</body>
</html>
--------------------------------------------------------------
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>admin</title>
</head>
<body>
<h1>hello,this is admin page, if you can see this page, you have admin role.</h1>
</body>
</html>
-----------------------------------------------------------------
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>user</title>
</head>
<body>
<h1>hello,this is user page, if you can see this page, you have user role.</h1>
</body>
</html>
UserDetailsServiceImpl 如下:
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserService userService;
//获取用户信息,返回UserDetails接口对象,
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
//获取数据库用户信息
UserDO dbUser = userService.getUserByName(userName);
System.out.println(dbUser.toString());
//获取数据库角色信息
List<UserRoleDO> roleList = userService.findRolesByUserName(userName);
return changeToUser(dbUser, roleList);
}
private UserDetails changeToUser(UserDO dbUser, List<UserRoleDO> roleList) {
//权限列表
List<GrantedAuthority> authorityList = new ArrayList<>();
//赋予查询到的角色
for (UserRoleDO role : roleList) {
GrantedAuthority authority = new SimpleGrantedAuthority(role.getRoleName());
authorityList.add(authority);
}
//创建UserDetails对象,设置用户名、密码和权限
return new org.springframework.security.core.userdetails.User(dbUser.getUserName(), dbUser.getPassword(), authorityList);
}
}
配置文件application.properties如下:
spring.datasource.url=jdbc:mysql://localhost:3306/你的数据库名?serverTimezone=GMT%2B8
spring.datasource.username=用户名
spring.datasource.password=密码
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.mapper-locations=mybatis/**/*Mapper.xml
mybatis.type-aliases-package=com.example.demo.**.domain
logging.level.com.example.demo.dao=debug
spring.thymeleaf.encoding=UTF-8