(六) SpringSecurity安全框架

6.1 什么是Spring Security

Spring Security,这是一种基于 Spring AOP 和 Servlet 过滤器的安全框架。它提供全面的安全性解决方案,同时在 Web 请求级和方法调用级处理身份确认和授权。

它是一个工鞥强大,可高度定制的身份验证和安全性的框架,它是保护基于Spring的应用程序的实时标准。它真正的威力在于它可以轻松地扩展以满足要求。

6.1.2整体框架(比较重要)

在这里插入图片描述

其核心就是一组过滤器链,项目启动后将会自动配置。最核心的就是 Basic Authentication Filter 用来认证用户的身份,一个在spring security中一种过滤器处理一种认证方式

Spring Security 认证(Authentication)和授权(Authorization)是分开的

6.1.3 认证(Authentication)和授权(Authorization)的区别

简单来说

  • 认证 (Authentication): 你是谁。

  • 授权 (Authorization): 你有权限干什么。

稍微正式点(啰嗦点)的说法就是 :

  • Authentication(认证) 是验证您的身份的凭据(例如用户名/用户 ID 和密码),通过这个凭据,系统得以知道你就是你,也就是说系统存在你这个用户。所以,Authentication 被称为身份/用户验证。

  • Authorization(授权) 发生在 Authentication(认证) 之后。授权嘛,光看意思大家应该就明白,它主要掌管我们访问系统的权限。比如有些特定资源只能具有特定权限的人才能访问比如 admin,有些对系统资源操作比如删除、添加、更新只能特定人才具有。

复杂专业的说法

认证

身份验证是关于验证您的凭据,如用户名/用户ID和密码,以验证您的身份。系统确定您是否就是您所说的使用凭据。在公共和专用网络中,系统通过登录密码验证用户身份。身份验证通常通过用户名和密码完成,有时与身份验证因素结合使用,后者指的是各种身份验证方式。

身份验证因素决定了系统在授予访问文件和请求银行交易之外的任何内容之前验证某人身份的各种要素。用户的身份可以通过他所知道的,他拥有的或者他是什么来确定。在安全性方面,必须至少验证两个或所有三个身份验证因素,以便授予某人访问系统的权限。

根据安全级别,身份验证因素可能与以下之一不同:

  • 单因素 身份验证 - 这是最简单的身份验证方法,通常依赖于简单的密码来授予用户对特定系统(如网站或网络)的访问权限。此人可以仅使用其中一个凭据请求访问系统以验证其身份。单因素身份验证的最常见示例是登录凭据,其仅需要针对用户名的密码。

  • 双因素身份验证 - 顾名思义,它是一个两步验证过程,不仅需要用户名和密码,还需要用户知道的东西,以确保更高级别的安全性,例如ATM引脚,用户知道。使用用户名和密码以及额外的机密信息,欺诈者几乎不可能窃取有价值的数据。

  • 多重身份验证 - 这是最先进的身份验证方法,它使用来自独立身份验证类别的两个或更多级别的安全性来授予用户对系统的访问权限。所有因素应相互独立,以消除系统中的任何漏洞。金融机构,银行和执法机构使用多因素身份验证来保护其数据和应用程序免受潜在威胁。

例如,当您将ATM卡输入ATM机时,机器会要求您输入您的PIN。在您正确输入引脚后,银行会确认您的身份证明该卡真正属于您,并且您是该卡的合法所有者。通过验证您的ATM卡引脚,银行实际上会验证您的身份,这称为身份验证。它只是确定你是谁,没有别的。

授权

另一方面,授权发生在系统成功验证您的身份后,最终会授予您访问资源(如信息,文件,数据库,资金,位置,几乎任何内容)的完全权限。简单来说,授权决定了您访问系统的能力以及达到的程度。验证成功后,系统验证您的身份后,即可授权您访问系统资源。

授权是确定经过身份验证的用户是否可以访问特定资源的过程。它验证您是否有权授予您访问信息,数据库,文件等资源的权限。授权通常在验证后确认您的权限。简单来说,就像给予某人官方许可做某事或任何事情。

例如,验证和确认组织中的员工ID和密码的过程称为身份验证,但确定哪个员工可以访问哪个楼层称为授权。假设您正在旅行而且即将登机。当您在登记前出示机票和一些身份证明时,您会收到一张登机牌,证明机场管理局已对您的身份进行了身份验证。但那不是它。乘务员必须授权您登上您应该乘坐的航班,让您可以进入飞机内部及其资源。

对系统的访问受身份验证和授权的保护。可以通过输入有效凭证来验证访问系统的任何尝试,但只有在成功授权后才能接受。如果尝试已通过身份验证但未获得授权,系统将拒绝访问系统。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rc91EKRU-1667651390143)(C:\Users\MrLiu\Desktop\培训课程学习\笔记\SpringBoot笔记\img\b5.png)]

摘要

虽然这两个术语经常相互结合使用,但它们的概念和含义完全不同。虽然这两个概念对于Web服务基础结构至关重要,特别是在授予对系统的访问权限时,理解关于安全性的每个术语是关键。虽然我们大多数人将一个术语与另一个术语混淆,但理解它们之间的关键区别很重要,实际上非常简单。如果身份验证是您的身份,则授权是您可以访问和修改的权限。简单来说,身份验证就是确定某人是否是他声称的人。另一方面,授权是确定他访问资源的权利。

举个例子来说:

你要登机,你需要出示你的身份证和机票,身份证是为了证明你张三确实是你张三,这就是 authentication;而机票是为了证明你张三确实买了票可以上飞机,这就是 authorization。

在网站认证领域再举个例子: 你要登陆论坛,输入用户名张三,密码1234,密码正确,证明你张三确实是张三,这就是 authentication;再一check用户张三是个版主,所以有权限加精删别人帖,这就是 authorization。

6.2 springSecurity使用

6.2.1 依赖导入

<?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.7.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.cn.wanxi</groupId>
    <artifactId>springboot-security</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-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.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

6.2.2编写配置文件

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
  security:
    user:
      name: lrs
      password: ***
mybatis:
  mapperLocations: classpath:mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true

使用security自带的登录验证窗口,通俗说就是你不用自己的我就代劳了,这里也可以不配置用户名密码,会在控制台生成密钥

6.2.3 编写实现类

该实体类需要去实现GrantedAuthority的接口并且重写getAuthority()方法,这个方法用于获取授权

package com.cn.wanxi.springbootsecurity.entity;

import org.springframework.security.core.GrantedAuthority;

import java.awt.*;
import java.util.Date;

/**
 * @Author MrLiu
 * @Date 2022/9/26 16:57
 * @Version 1.0
 */
public class Permission2 implements GrantedAuthority {
    private Integer id;
    private Integer pid;
    private String name;
    private String value;
    private String icon;
    private Integer type;
    private String uri;
    private Integer status;
    private Date createTime;
    private String sort;

    @Override
    public String getAuthority() {
        return this.value;
    }

    public Integer getId() {
        return id;
    }

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

    public Integer getPid() {
        return pid;
    }

    public void setPid(Integer pid) {
        this.pid = pid;
    }

    public String getName() {
        return name;
    }

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

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getIcon() {
        return icon;
    }

    public void setIcon(String icon) {
        this.icon = icon;
    }

    public Integer getType() {
        return type;
    }

    public void setType(Integer type) {
        this.type = type;
    }

    public String getUri() {
        return uri;
    }

    public void setUri(String uri) {
        this.uri = uri;
    }

    public Integer getStatus() {
        return status;
    }
    public void setStatus(Integer status) {
        this.status = status;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public String getSort() {
        return sort;
    }

    public void setSort(String sort) {
        this.sort = sort;
    }
}

该实体类对应用户表需要实现UserDetails接口并重写getAuthorities()方法用于获取认证的所有授权

package com.cn.wanxi.springbootsecurity.entity;

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

import java.util.Collection;
import java.util.Date;
import java.util.Set;

/**
 * @Author MrLiu
 * @Date 2022/9/26 16:52
 * @Version 1.0
 */
public class WxUserEntity implements UserDetails {
    private Integer id;
    private String username;
    private String password;
    private String icon;
    private String email;
    private String nickName;
    private String note;
    private Date createTime;
    private Date loginTime;
    private Integer status;

    private Set<? extends GrantedAuthority> authorities;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.authorities;
    }

    public void setAuthorities(Set<? extends GrantedAuthority> authorities) {
        this.authorities = authorities;
    }

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

    @Override
    public String getUsername() {
        return this.username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

    public Integer getId() {
        return id;
    }

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

    public void setUsername(String username) {
        this.username = username;
    }

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

    public String getIcon() {
        return icon;
    }

    public void setIcon(String icon) {
        this.icon = icon;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Date getLoginTime() {
        return loginTime;
    }

    public void setLoginTime(Date loginTime) {
        this.loginTime = loginTime;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    @Override
    public String toString() {
        return "WxUserEntity{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", icon='" + icon + '\'' +
                ", email='" + email + '\'' +
                ", nickName='" + nickName + '\'' +
                ", note='" + note + '\'' +
                ", createTime=" + createTime +
                ", loginTime=" + loginTime +
                ", status=" + status +
                '}';
    }
}

6.2.4 编写持久层

两个分别是查用户和根据用户id查授权表

@Mapper
public interface WxUserMapper {
    List<WxUserEntity> getUserByName(String name);

    List<Permission2> getPermissionByUserId(Integer userId);
}
<?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.cn.wanxi.springbootsecurity.dao.WxUserMapper">
    <select id="getUserByName" resultType="com.cn.wanxi.springbootsecurity.entity.WxUserEntity">
        select * from  wx_user where  username =#{name}
    </select>
    <select id="getPermissionByUserId" resultType="com.cn.wanxi.springbootsecurity.entity.Permission2">
        SELECT * FROM wx_permission as wxp INNER JOIN (SELECT permission_id from wx_role_permission_relation as rpr INNER JOIN wx_user_role_relation as urr where
        rpr.role_id = urr.role_id and urr.admin_id = 3) as per on wxp.id = permission_id
    </select>
</mapper>

6.2.5 业务逻辑层

该接口为用户的业务接口

package com.cn.wanxi.springbootsecurity.service;

/**
 * @Author MrLiu
 * @Date 2022/9/26 17:31
 * @Version 1.0
 */

import com.cn.wanxi.springbootsecurity.entity.Permission2;
import com.cn.wanxi.springbootsecurity.entity.WxUserEntity;

import java.util.List;

/**
 * @author MrLiu
 * @description: 业务相关的Service,实现自己的业务逻辑处理。
 */
public interface WxUserService {

    /**
     * 根据用户名获取用户对象
     * @param name 用户登录名
     * @return
     */
    WxUserEntity getUserByName(String name);

    /**
     * 获取指定用户拥有的权限
     * @param userId 用户id
     * @return
     */
    List<Permission2> getPermissionsByUserId(Integer userId);
}

实现接口

package com.cn.wanxi.springbootsecurity.service.Impl;

import com.cn.wanxi.springbootsecurity.dao.WxUserMapper;
import com.cn.wanxi.springbootsecurity.entity.Permission2;
import com.cn.wanxi.springbootsecurity.entity.WxUserEntity;
import com.cn.wanxi.springbootsecurity.service.WxUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.List;

/**
 * @Author MrLiu
 * @Date 2022/9/26 17:32
 * @Version 1.0
 */
@Service
public class WxUserServiceImpl implements WxUserService {
    @Autowired
    WxUserMapper wxUserMapper;
    @Override
    public WxUserEntity getUserByName(String name) {
        List<WxUserEntity> list=wxUserMapper.getUserByName(name);
        Assert.isTrue(list.size()==1,"您输入的账户不存在,或者有多个相同的账户");
        return list.get(0);
    }

    @Override
    public List<Permission2> getPermissionsByUserId(Integer userId) {
        // 获取权限
        return wxUserMapper.getPermissionByUserId(userId);
    }
}

授权表业务逻辑,需要实现UserDetailsService接口并重写loadUserByUsername(String username)方法

package com.cn.wanxi.springbootsecurity.service;

import com.cn.wanxi.springbootsecurity.entity.Permission2;
import com.cn.wanxi.springbootsecurity.entity.WxUserEntity;
import org.springframework.beans.factory.annotation.Autowired;
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 java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @Author MrLiu
 * @Date 2022/9/26 18:14
 * @Version 1.0
 */
@Service
public class MyUserDetailService implements UserDetailsService {
    @Autowired
    WxUserService wxUserService;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        WxUserEntity wxUserEntity=wxUserService.getUserByName(username);
        List<Permission2> list=wxUserService.getPermissionsByUserId(wxUserEntity.getId());
        HashSet<Permission2> set=new HashSet<>(list);
        wxUserEntity.setAuthorities(set);
        return wxUserEntity;
    }
}

6.2.6 控制层

package com.cn.wanxi.springbootsecurity.controller;

import com.cn.wanxi.springbootsecurity.entity.WxUserEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;

/**
 * @Author MrLiu
 * @Date 2022/9/26 18:29
 * @Version 1.0
 */
@RestController
@RequestMapping("/product")
public class ProductController {

    // 这里配置的内容是数据库里,表示有这个权限的才能使用这个方法。
    // 这个注解要自定义的Configure类开启了@EnableGlobalMethodSecurity才有效
    @PreAuthorize("hasAuthority('wx:product:read')")
    @RequestMapping("/phones")
    public String getPhones(HttpSession httpSession) {
//        SecurityContext securityContext = (SecurityContext) httpSession.getAttribute("SPRING_SECURITY_CONTEXT");
        /*Spring Security会将SecurityContextHolder里的数据保存到Session中,
         SecurityContextHolder 存储的是SecurityContext对象
         SecurityContext 存储的是Authentication对象*/
        SecurityContext securityContext = SecurityContextHolder.getContext();
        WxUserEntity user = (WxUserEntity) securityContext.getAuthentication().getPrincipal();
        String info = user + "";
        return info;
    }
}

6.2.7 配置类

这里我们用自己的登录方式

package com.cn.wanxi.springbootsecurity.config;

import com.cn.wanxi.springbootsecurity.service.MyUserDetailService;
import com.cn.wanxi.springbootsecurity.service.SimpleUserDetailsService;
import com.cn.wanxi.springbootsecurity.service.WxUserService;
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.SecurityBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @Author MrLiu
 * @Date 2022/9/26 15:04
 * @Version 1.0
 */
@Configuration
// 必须要有,否则权限不起作用
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfigure extends WebSecurityConfigurerAdapter  {
    @Autowired
    private MyUserDetailService myUserDetailService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 第一步,我们实现了一个当访问users资源时不需要进行验证。
        http.authorizeRequests()
                //放行
                .antMatchers("/users").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and()
//                .userDetailsService(userDetailsService);
                .userDetailsService(myUserDetailService);
    }

    // 这个要有,框架要对密码做加密,不能是明文。

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

🤳博主主页:Copping0606 😁
博主定期更新🤦‍♂️
谢谢你那么好看还关注我💖

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coping0606

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值