spring security入门--从数据库读取数据实现用户登录访问简单示例四

发布时间:2022-12-08 23:30

1.说明

之前的几个示例都是从内存中获取的数据,这里改写为从数据库获取数据实现用户登录访问功能

从内存读取数据的代码可参看

地址:https://blog.csdn.net/qq_32224047/article/details/108604598

2.代码示例

数据库建表语句

CREATE DATABASE /*!32312 IF NOT EXISTS*/`security` /*!40100 DEFAULT CHARACTER SET utf8 */;

USE `security`;

 

DROP TABLE IF EXISTS `tb_permission`;

 

CREATE TABLE `tb_permission` (

  `id` BIGINT(20) NOT NULL AUTO_INCREMENT,

  `user_id` BIGINT(20) DEFAULT NULL COMMENT '父权限',

  `name` VARCHAR(64) NOT NULL COMMENT '权限名称',

  `authority` VARCHAR(64) NOT NULL COMMENT '权限英文名称',

  `created` DATETIME NOT NULL,

  `updated` DATETIME NOT NULL,

  PRIMARY KEY (`id`)

) ENGINE=INNODB AUTO_INCREMENT=49 DEFAULT CHARSET=utf8 COMMENT='权限表';

 

/*Data for the table `tb_permission` */

 

INSERT  INTO `tb_permission`(`id`,`user_id`,`name`,`authority`,`created`,`updated`) VALUES (1,1,'写入','write','2019-06-07 15:00:00','2019-06-07 15:00:00'),(2,1,'更新','update','2019-06-07 15:00:00','2019-06-07 15:00:00'),(3,1,'删除','delete','2019-06-07 15:00:00','2019-06-07 15:00:00'),(4,1,'读取','read','2019-06-07 15:00:00','2019-06-07 15:00:00'),(5,2,'读取','read','2019-06-07 15:00:00','2019-06-07 15:00:00');

 

/*Table structure for table `tb_user` */

 

DROP TABLE IF EXISTS `tb_user`;

 

CREATE TABLE `tb_user` (

  `id` BIGINT(20) NOT NULL AUTO_INCREMENT,

  `username` VARCHAR(50) NOT NULL COMMENT '用户名',

  `password` VARCHAR(64) NOT NULL COMMENT '密码,加密存储',

  `phone` VARCHAR(20) DEFAULT NULL COMMENT '注册手机号',

  `created` DATETIME NOT NULL,

  `updated` DATETIME NOT NULL,

  PRIMARY KEY (`id`),

  UNIQUE KEY `username` (`username`) USING BTREE,

  UNIQUE KEY `phone` (`phone`) USING BTREE

) ENGINE=INNODB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8 COMMENT='用户表';

 

/*Data for the table `tb_user` */

 

INSERT  INTO `tb_user`(`id`,`username`,`password`,`phone`,`created`,`updated`) VALUES (1,'admin','123456','18510270606','2019-06-15 10:00:58','2019-06-15 10:00:58'),(2,'user','123456','18610270607','2019-06-15 10:00:58','2019-06-15 10:00:58');

表的机构信息如下图

spring security入门--从数据库读取数据实现用户登录访问简单示例四_第1张图片

spring security入门--从数据库读取数据实现用户登录访问简单示例四_第2张图片

项目结构如下

spring security入门--从数据库读取数据实现用户登录访问简单示例四_第3张图片

启动类StarterSecurity1 添加了Mapper包的扫描


@SpringBootApplication
@MapperScan("cn.wy.mapper")
public class StarterSecurity1 {
    public static void main(String[] args) {
        SpringApplication.run(StarterSecurity1.class,args);
    }
}

配置文件数据

#datasource配置
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql:///security?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

#mybatis
mybatis.type-aliases-package=cn.wy.domain
#将带有下划线的表字段映射为驼峰格式的实体类属性
#添加了该项配置后,在开发中只需要根据查询返回的字段,创建好实体类就可以了
mybatis.configuration.map-underscore-to-camel-case=true

控制器类AdminController

@RestController
public class AdminController {
    @RequestMapping("/admin/write")
    public String write() {
        return "写入数据";
    }

    @RequestMapping("/admin/update")
    public String update() {
        return "更新数据";
    }

    @RequestMapping("/admin/delete")
    public String delete() {
        return "删除数据";
    }

    @RequestMapping("/user/read")
    public String read() {
        return "读取数据,不需要权限";
    }
    @RequestMapping("/other")
    public String other() {
        return "其他权限";
    }
}

配置类MyWebSecurityConfig

import cn.wy.service.UserService;
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.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class MyWebSecurityConfig
        extends WebSecurityConfigurerAdapter {
    //明文加密器,只需要在内存中有这个管理对象,如果不添加,从前端登录时会抛出异常
    @Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }
    //构造一个内存框架对象,获取数据库中的数据
    @Bean
    public UserDetailsService myUserDetailsService(){
        return new UserService();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //获取数据库中的数据
        auth.userDetailsService(myUserDetailsService());

    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()// 开启权限控制,通过ANT规范,自定义逻辑

                //当访问/admin/write时必须有write权限
                .antMatchers("/admin/write").hasAuthority("write")
                .antMatchers("/admin/update").hasAuthority("update")
                .antMatchers("/admin/delete").hasAuthority("delete")
                .antMatchers("/user/read").hasAuthority("read")
            .anyRequest()//任意请求
            .authenticated();//必须经过认证 只要登陆就能访问
        http.formLogin();//开启表单认证
        http.httpBasic();//开启http 基本认证
    }

}

实体类User和Permission类

package cn.wy.domain;

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

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.List;

public class User implements Serializable {
    private Long id;
    private String username;
    private String password;
    private String phone;
    private Date created;
    private Date updated;

    private List authorities;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
    @Override
    public String getUsername() {
        return username;
    }
    
    public void setUsername(String username) {
        this.username = username;
    }
    public void setAuthorities(List authorities) {
        this.authorities = authorities;
    }

    @Override
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public Date getCreated() {
        return created;
    }

    public void setCreated(Date created) {
        this.created = created;
    }

    public Date getUpdated() {
        return updated;
    }

    public void setUpdated(Date updated) {
        this.updated = updated;
    }


}

 

package cn.wy.domain;

import java.io.Serializable;
import java.util.Date;

public class Permission implements Serializable {
    private Long id;
    private Long userId;
    private String name;
    private String authority;
    private Date created;
    private Date updated;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAuthority() {
        return authority;
    }

    public void setAuthority(String authority) {
        this.authority = authority;
    }

    public Date getCreated() {
        return created;
    }

    public void setCreated(Date created) {
        this.created = created;
    }

    public Date getUpdated() {
        return updated;
    }

    public void setUpdated(Date updated) {
        this.updated = updated;
    }
}

接口类UserMapper、PermissionMapper

直接在接口类上编写sql语句,不在创建对应的映射文件,注意sql 语句中换行后要添加一个空格,防止代码粘粘在一起,导致sql语句错误

package cn.wy.mapper;

import cn.wy.domain.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

public interface UserMapper {
    //利用username读取user对象
    @Select("select * from tb_user " +
            "where username=#{username}")
    public User selectUserByUsername(
            @Param("username") String username);
}
package cn.wy.mapper;

import cn.wy.domain.Permission;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface PermissionMapper {
    //利用user_id搜索的对应permission list
    //注意 下面的sql中表名和 where 之间需要加一个空格,否则会粘粘在一起,导致sql语句错误
    @Select("select * from tb_permission" +
            " where user_id=#{userId}")
    public List selectPermissionsByUserid(
            @Param("userId") Long userId);
}

UserService类用于读取Mapper数据库数据,要和Security整合,需要实现接口UserDetailsService,注意自定义的User和Security提供的User

package cn.wy.service;


import cn.wy.domain.Permission;
import cn.wy.mapper.PermissionMapper;
import cn.wy.mapper.UserMapper;
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 java.util.ArrayList;
import java.util.List;

/**
 * 用于读取Mapper数据库数据,要和Security整合,需要
 * 实现接口UserDetailsService
 *
 */
public class UserService implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PermissionMapper permissionMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //username参数,是在登陆时,用户传递的表单数据username
        //主要读取数据库3个值 username password authorities
        //这里自定义的User和security中的User重名,且那个包名很长所以这里写自定义User的包路径
        cn.wy.domain.User user = userMapper.selectUserByUsername(username);
        Long userId=user.getId();
        List permissions = permissionMapper.selectPermissionsByUserid(userId);
        //为了返回一个UserDetails 使用User
        List authorities=new ArrayList<>();
        for (Permission p:permissions) {
            //循环一次,就拿到了表格权限,使用authority字段
            String authorityName = p.getAuthority();
            GrantedAuthority authority=
                    new SimpleGrantedAuthority(authorityName);
            authorities.add(authority);
        }

    //这里的User 是这个包下的 org.springframework.security.core.userdetails.User;
    User userDetails=new User(
                user.getUsername(),
                user.getPassword(),
                authorities);
        user.setAuthorities(authorities);
        return userDetails;
    }

}

测试验证:

登录user访问write功能

spring security入门--从数据库读取数据实现用户登录访问简单示例四_第4张图片

测试正常,无权限 

spring security入门--从数据库读取数据实现用户登录访问简单示例四_第5张图片

更换为admin用户

spring security入门--从数据库读取数据实现用户登录访问简单示例四_第6张图片

测试正确

spring security入门--从数据库读取数据实现用户登录访问简单示例四_第7张图片

其他测试均正常,这里不再列出

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号