SpringSecurity数据库进行认证和授权的使用

网友投稿 959 2022-12-17

SpringSecurity数据库进行认证和授权的使用

SpringSecurity数据库进行认证和授权的使用

目录一、准备工作1.1 导入相关依赖1.2 配置信息1.3 数据库准备1.4 实体类的创建1.5 Dao层的创建1.6 Service层的编写1.7 Security配置1.8 密码加密1.9 测试结果

在前面的文章中,我们介绍了Spring Security基于内存的一些基本使用方法,但在真实的业务场景中,用户的账号、密码以及角色信息肯定都是存放在数据库中的,所以我们需要从数据库中来加载认证和授权的数据。

一、准备工作

如下案例是基于上一篇中的案例改造而来,所以建议先阅读前一篇文章的内容,将基本案例的代码准备好。

1.1 导入相关依赖

除了引入security的依赖外,我们还需要引入数据库驱动、连接池、MyBatis的依赖包:

org.springframework.boot

spring-boot-starter-security

org.springframework.boot

spring-boot-starter-web

org.projectlombok

lombok

true

mysql

mysql-connector-java

8.0.25

com.alibaba

druid-spring-boot-starter

1.2.6

org.mybatis.spring.boot

mybatis-spring-boot-starter

2.2.0

1.2 配置信息

我们需要在配置文件中增加数据源的配置信息,指定mapper xml的位置,这里改成你自己的位置即可。

# 配置数据源信息

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.datasource.url=jdbc:mysql://x.x.x.x:3306/zhangxun?characterEncoding=utf-8&&serverTimezone=Asia/Shanghai&&useSSL=false

spring.datasource.username=root

spring.datasource.password=root

# 配置mapper xml的位置

mybatis.mapper-locations=classpath:mapper/*Mapper.xml

# 是否显示执行sql

logging.level.com.xun.mapper=debug

1.3 数据库准备

我们需要创建如下三张表,分别用来存储用户信息、角色信息、用户与角色的对应关系。

CREATE TABLE `auth_user` (

`user_id` int(11) NOT NULL AUTO_INCREMENT,

`user_name` varchar(100) DEFAULT NULL,

`password` varchar(100) DEFAULT NULL,

`expired` tinyint(1) DEFAULT NULL,

`locked` tinyint(1) DEFAULT NULL,

PRIMARY KEY (`user_id`)

) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4

-- 密文密码的生成逻辑下面1.8节会讲到,可以替换为自己密码的密文进行插入

INSERT INTO zhangxun.auth_user (user_id, user_name, password, expired, locked) VALUES(1, 'root', '$2a$10$Hv037iGDdj82YjFORlYnyOdlra2EObV2XdddyW8A.r.Ph5ETOBNo2', 0, 0);

INSERT INTO zhangxun.auth_user (user_id, user_name, password, expired, locked) VALUES(2, 'zhang', '$2a$10$cVgFLGx0Crz0Jf2TDBhJ.e6FS.BpH7YOoox2iSJrGW1DJ6OXiOt86', 0, 0);

CREATE TABLE `auth_role` (

`role_id` int(11) NOT NULL AUTO_INCREMENT,

`role_code` varchar(100) DEFAULT NULL,

`role_name` varchar(100) DEFAULT NULL,

PRIMARY KEY (`role_id`)

) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

INSERT INTO zhangxun.auth_role (role_id, role_code, role_name) VALUES(1, 'admin', '管理员');

INSERT INTO zhangxun.auth_role (role_id, role_code, role_name) VALUES(2, 'manager', '经理');

CREATE TABLE `auth_user_role` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`user_id` int(11) DEFAULT NULL,

`role_code` varchar(100) DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;

-- root用户将拥有admin和manager两个角色,zhang用户仅拥有manager一个角色

INSERT INTO zhangxun.auth_user_role (id, user_id, role_code) VALUES(1, 1, 'admin');

INSERT INTO zhangxun.auth_user_role (id, user_id, role_code) VALUES(2, 1, 'manager');

INSERT INTO zhangxun.auth_user_role (id, user_id, role_code) VALUES(3, 2, 'manager');

1.4 实体类的创建

@Data

public class Role {

private Integer roleId;

private String roleCode;

private String roleName;

}

@Data

public class User implements UserDetails {

private Integer userId;

private String userName;

private String password;

private Integer expired;

private Integer locked;

private List roles;

/**

* 获取用户的所有角色信息

* @return

*/

@Override

public Collection extends GrantedAuthority> getAuthorities() {

List authorities = new ArrayList<>();

for(Role role : roles){

// 也可以在数据中添加角色时,就以 ROLE_ 开始,这样就不用二次添加了

authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleCode()));

}

return authorities;

}

/**

* 指定哪一个是用户的密码字段

* @return

*/

@Override

public String getPassword() {

rethttp://urn password;

}

/**

* 指定哪一个是用户的账户字段

* @return

*/

@Override

public String getUsername() {

return userName;

}

/**

* 判断账户是否过期

* @return

*/

@Override

public boolean isAccountNonExpired() {

return (expired == 0);

}

/**

* 判断账户是否锁定

* @return

*/

@Override

public boolean isAccountNonLocked() {

return (locked == 0);

}

/**

* 判断密码是否过期

* 可以根据业务逻辑或者数据库字段来决定

* @return

*/

@Override

public boolean isCredentialsNonExpired() {

return true;

}

/**

* 判断账户是否可用

* 可以根据业务逻辑或者数据库字段来决定

* @return

*/

@Override

public boolean isEnabled() {

return true;

}

}

实现UserDetails接口的实体类会被认为是User实体,Spring Security会根据重写的方法来加载用户的必要信息:账户信息、密码信息、账户过期、账户锁定、密码过期、账户启用、账户拥有的角色信息。

1.5 Dao层的创建

@Mapper

public interface UserMapper {

User getUserByUserName(String userName);

List getUserRolesByUserId(Integer userId);

}

select

au.user_id userId,

au.user_name userName,

au.password,

au.expired,

au.locked

from

auth_user au

where

au.user_name = #{userName}

select

ar.role_id roleId,

ar.role_code roleCode,

ar.role_name roleName

from

auth_user_role aur

lefhttp://t join auth_role ar on

aur.role_code = ar.role_code

where

aur.user_id = #{userId}

注意,如果UserMapper接口你是用了@Repository注解,那么就需要在启动类上加上Mapper所在包的位置。

@MapperScan("com.example.securitydemo.mapper")

1.6 Service层的编写

@Slf4j

@Service

public class UserService implements UserDetailsService {

@Resource

private UserMapper userMapper;

/**

* 根据用户名去数据库获取用户信息,SpringSecutity会自动进行密码的比对

* @param username

* @return

* @throws UsernameNotFoundException

*/

@Override

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

// 用户名必须是唯一的,不允许重复

User user = userMapper.getUserByUserName(username);

if(ObjectUtils.isEmpty(user)){

throw new UsernameNotFoundException("根据用户名找不到该用户的信息!");

}

List roleList = userMapper.getUserRolesByUserId(user.getUserId());

user.setRoles(roleList);

return user;

}

}

1.7 Security配置

@EnableWebSecurity

public class DBSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired

private UserService userService;

/**

* 对请求进行鉴权的配置

*

* @param http

* @throws Exception

*/

@Override

protected void configure(HttpSecurity http) throws Exception {

http.authorizeRequests()

// 任何角色允许访问

.antMatchers("/", "/index").permitAll()

// 仅admin角色可以访问

.antMatchers("/admin/**").hasRole("admin")

// admin和manager两个角色可以访问

.antMatchers("/manager/**").hasAnyRole("admin", "manager")

.and()

// 没有权限进入内置的登录页面

.formLogin()

.and()

// 暂时关闭CSRF校验,允许get请求登出

.csrf().disable();

}

@Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

auth.userDetailsService(userService);

}

/**

* 默认开启密码加密,前端传入的密码Security会在加密后和数据库中的密文进行比对,一致的话就登录成功

* 所以必须提供一个加密对象,供security加密前端明文密码使用

* @return

*/

@Bean

PasswordEncoder passwordEncoder() {

return new BCryptPasswordEncoder();

}

}

1.8 密码加密

@Slf4j

public class PasswordEncode {

private PasswordEncoder passwordEncoder;

@Before

public void before(){

this.passwordEncoder = new BCryptPasswordEncoder();

}

@Test

public void encodePassword(){

String rawPassword = ofwjUe"mm000";

String encodePassword = passwordEncoder.encode(rawPassword);

log.info("password:{} encoded is: {}", rawPassword, encodePassword);

}

}

该类是一个测试类,为了将明文密码加密后得到密文的,密文存储到User表的password字段。Spring Security默认开启密码加密功能的,数据库加载出来的密码会被进行格式校验,如果不是合法的密文,登录逻辑就会失败。

1.9 测试结果

访问localhost:8080/index无需登录,直接就能返回结果。

访问localhost:8080/admin/getHello将跳转到登录页面,使用root账户(包含admin和manager两个角色),随便输入一个密码,登录失败;输入正确的密码,登录成功后返回正确的内容;此时再访问localhost:8080/manager/getHello也能获得正确的内容。

调用localhost:8080/logout后将退出登录,此时使用zhang账户(仅包含manager角色),输入正确的密码,登录成功后返回正确的内容;此时再访问localhost:8080/admin/getHello将无法获得内容。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SpringBoot2.x 参数校验问题小结
下一篇:SpringAop切入点execution表达式的深入讲解
相关文章

 发表评论

暂时没有评论,来抢沙发吧~