在用户以及权限硬编码的基础上整改,将用户以及用户的权限修改成从数据库查询.
硬编码教程SpringCloud-Secruity入门开发(用户以及权限硬编码篇)
具体实现
添加依赖
新增mybatis的依赖,链接数据库的mysql驱动依赖以及lombok的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.15.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.nlx</groupId>
<artifactId>springcloud-security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloud-security</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- mybatis依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
lombok只是为了待会儿pojo类不用 写getter以及setter等方法而添加的工具插件,不是必须有的依赖.
数据库创建用户及权限表
为了图省事,这里只创建一个用户表,但是表里包含了权限,实际开发中一般不这么做,而是将用户跟权限分开做绑定
CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
`password` varchar(64) DEFAULT NULL,
`authruity` varchar(64) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
创建pojo/mapper/mapper.xml
user.java
package com.nlx.pojo;
import java.io.Serializable;
import lombok.Data;
@Data
public class User implements Serializable{
private static final long serialVersionUID = 1L;
private int id;
private String name;
private String password;
private String authruity;
}
UserMapper.java
package com.nlx.mapper;
import org.apache.ibatis.annotations.Mapper;
import com.nlx.pojo.User;
@Mapper
public interface UserMapper {
User selectByName(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="com.nlx.mapper.UserMapper">
<select id="selectByName" resultType="com.nlx.pojo.User" >
select * from user where name=#{username}
</select>
</mapper>
配置数据链接 以及mybatis的包扫描和mapper.xml位置
mybatis.mapper-locations=classpath:mapper/*Mapper.xml
mybatis.type-aliases-package=com.nlx.pojo
spring.datasource.username=root
spring.datasource.password=nlx@123
spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
新建service.impl包,并在包里新建实现类,实现UserDetailsService接口
package com.nlx.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
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.stereotype.Service;
import com.nlx.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 从数据库查询用户
com.nlx.pojo.User selectByName = userMapper.selectByName(username);
log.info("用戶名:" + username);
// 如果用户不为null
if (selectByName != null) {
log.info("用戶名权限:" + selectByName.getAuthruity());
log.info("用戶密码:" + selectByName.getPassword());
// 这里的user是org.springframework.security.core.userdetails.User;
return new User(username, selectByName.getPassword(),
AuthorityUtils.commaSeparatedStringToAuthorityList(selectByName.getAuthruity()));
//return new User(username, selectByName.getPassword(),
// 多个权限的格式
// AuthorityUtils.commaSeparatedStringToAuthorityList("p1,p2,p3"));
}
// 如果没有这个用户 返回null 由secruity处理异常
return null;
}
}
注入UserMapper从数据库根据用户名查密码以及权限,然后将用户名,密码,权限赋值给UserDetails的子类
org.springframework.security.core.userdetails.User里面 返回就可以,当有多个权限时,用逗号隔开,
配置类MyWebSecruityConfig需要修改的地方如下
package com.nlx.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class MyWebSecruityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// 关闭csrf
.csrf().disable()
.authorizeRequests()
// 添加权限
.antMatchers("/insert").hasAuthority("p1")
.antMatchers("/delete").hasAuthority("p2")
// 其他不匹配上面的验证规则的请求都需要被验证
.anyRequest().authenticated()
// permitAll()无条件允许访问
// 开启session管理
// .and().sessionManagement()
.and().logout()
// 基本认证
// .and().httpBasic();
// 表单认证
.and().formLogin();
}
/*
@Bean
protected UserDetailsService userDetailsService() {
InMemoryUserDetailsManager im = new InMemoryUserDetailsManager();
im.createUser(User.withUsername("zhansan").password("123").authorities("p1").build());
im.createUser(User.withUsername("lisi").password("123").authorities("p2").build());
return im;
}*/
@Bean
PasswordEncoder passwordEncoder() {
/**
* secriuty默认对密码进行了BCrypt加密
* 我们这里暂时不加密密码
*/
//return new BCryptPasswordEncoder();
return NoOpPasswordEncoder.getInstance();
}
}
注释或者删除userDetailsService方法即可。
测试
数据库添加用户及权限
重启项目
由上图可以看出我们给用户test分配了p1权限,而访问/delete需要p2权限,如果test用户访问不到/delete,但是能访问到其他的接口则表示我们的配置是ok的
浏览器访问http://localhost:8080/delete,跳转登录后的访问结果如下。
其他三个接口的访问结果如下
总结
在上面的4个步骤中只有第四步是核心,简单概括就是添加一个实现类MyUserDetailsService实现UserDetailsService接口
并重写方法 loadUserByUsername(String username)我们在这个方法里面去查数据库返回密码以及权限封装给
org.springframework.security.core.userdetails.User返回即可,至于验证的过程交给Secruity去做。这里添加了这个实现,就需要我们删除在配置文件写死在内存中的用户。
关于密码加密问题:
secruity默认使用的加密是BCryptPasswordEncoder加密,如果配置文件中配置了加密方式(一定要配置加密方式),相对应的 如果我们存在数据库的密码是明文,则返回验证的时候需要手动加密,否则匹配不上会一直报密码或用户名错误。
package com.nlx.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class MyWebSecruityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// 关闭csrf
.csrf().disable()
.authorizeRequests()
// 添加权限
.antMatchers("/insert").hasAuthority("p1")
.antMatchers("/delete").hasAuthority("p2")
// 其他不匹配上面的验证规则的请求都需要被验证
.anyRequest().authenticated()
// permitAll()无条件允许访问
// 开启session管理
// .and().sessionManagement()
.and().logout()
// 基本认证
// .and().httpBasic();
// 表单认证
.and().formLogin();
}
//这个bean不能少否则后台报错
@Bean
PasswordEncoder passwordEncoder() {
//secriuty默认对密码进行了BCrypt加密
return new BCryptPasswordEncoder();
}
}
如果我们配置文件对密码加密配置为BCryptPasswordEncoder,那么在MyUserDetailsService中就需要对密码进行加密(因为我们存数据库的密码是明文的,不是BCryptPasswordEncoder加密的)
package com.nlx.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
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.Service;
import com.nlx.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
//注入配置文件密码加密的bean
@Autowired
private PasswordEncoder encode;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
com.nlx.pojo.User selectByName = userMapper.selectByName(username);
log.info("用戶名:" + username);
if (selectByName != null) {
log.info("用戶名权限:" + selectByName.getAuthruity());
log.info("用戶密码:" + selectByName.getPassword());
// 在这里的密码进行加密
return new User(username, encode.encode(selectByName.getPassword()),
AuthorityUtils.commaSeparatedStringToAuthorityList(selectByName.getAuthruity()));
}
return null;
}
}
修改的地方是注入的配置文件里面创建的用于密码加密的bean以及将数据库查出来的明文密码进行加密后再封装。