Spring Security OAuth2.0认证授权学习与使用~(更新中)

1.1 什么是认证

简单的例子(粗浅的理解):
		1、你去ATM取钱需要密码
		2、你去买票需要身份证
		3、你去坐高铁需要对应车票
		4、你登录QQ,需要输入账号密码
		5、认证类似一个物品或*的唯一标识

1.1.1 系统为什么要认证?

认证是为了保护系统的隐私数据与资源,只有用户拥有合法的身份才能访问该系统的资源。

1.1.2 认证

用户认证就是判断一个用户的身份是否合法的过程,用户去访问系统资源时,
系统要求验证用户的身份信息,身法合法方可继续访问,不合法则拒绝访问。
常见的用户身份认证方式有:
		1、用户名密码登录认证
		2、二维码登录认证
		3、手机短信登录认证
		4、指纹认证等方式

1.2 什么是会话?

用户认证通过后,为了避免用户每次操作都进行认证,可将用户的信息保存在会话中。

1.2.1 会话

会话就是系统为了保持当前用户的登录状态所提供的机制,常见的有基于session方式、基于token方式等

1.2.2 基于Session的认证方式如下图(根据图中步骤):

他的交互流程是,用户认证成功后,在服务端生成用户相关信息的数据保存在session(当前会话)中,
发给客户端的session_id存放到cookid中,
这样用户客户端请求时带上session_id就可以验证服务器端是否存在session数据,
以此完成用户的合法校验,当前用户推出系统或session过期销毁时,客户端的session_id也就无效了

session认证示意图

1.2.3 基于token认证方式如下图(根据图中步骤):

它的交互流程是,基于用户认证成功后,服务端生成一个token发给客户端,
客户端可以放到cookie或localStorage(用来存储客户端临时信息的对象)等存储中,
每次请求时带上token,服务端收到token通过验证后即可确认用户身份。

token验证示意图

1.2.4 session和token的区别

基于sess的认证方式由Servlet规范定制,服务端要存储sess信息需要真用内存资源,客户端需要支持cookie;
基于token的方式则一般不需要服务端存储token,并且不限制客户端的存储方式。

如今移动互联网时代更多类型的客户端需要接入系统,系统多此采用的前后端分离的架构进行实现,所以基于token
的方式更适合当下。

1.3 什么是授权?

拿微信来举例子,微信登录成功后用户即可使用微信的功能,
比如:发红包、发朋友圈、添加好友等,没有绑定银行卡的用户是无法发送红包的,绑定银行卡的用户才可以发红包,
发红包功能、发欧阳泉功能都是微信的资源即功能资源,用户拥有发红包功能的权限才可以正常使用发红包功能,
拥有发朋友圈功能的权限才可以使用发朋友圈功能,这个根据用户来控制用户使用资源你的过程是授权。

1.3.1 为什么要授权?

认证是为了保护用户身份的合法性,授权则是为了更烦细粒度的对隐私数据进行划分,授权是在认证通过后发生的
控制不同的用户能够访问不同的资源。

1.3.2 授权

授权是用户认证通过根据用户的权限来控制用户访问资源的过程,拥有资源你的访问权限则正常访问,没有权限则
拒绝访问。

1.4 授权的数据模型

如何进行授权即如何对用户访问资源进行控制,首先需要学习授权相关的数据模型、

授权可简单理解微Who对What(which)进行How操作,包括如下:
	Who,即主体(Subject),主体一般是指用户,也可以是程序,也可以是需要访问系统中的资源等。
	What,即资源(Resource),如系统菜单、页面、按钮、代码方法、系统商品信息、系统订单信息等。系统菜单、
页面、按钮、代码方法都属于系统功能资源,对于web系统每个功能资源通常对应一个URL;系统商品信息、系统
订单信息都属于实体资源(数据资源),实体资源有资源类型和资源实例组成,比如商品信息微资源类型,商品编号
为001的商品为资源实例等(可能不好理解,就看做资源是类,实例是你new了一个类)。
	How,权限/许可(permission),规定了用户对资源的操作许可,权限离开资源没有意义,如用户查新权限、
	用户添加权限、某个代码方法的调用权限、编号为001的用户的修改权限等,通过权限可知用户对那些资源都有那些
操作许可。

主体、资源、权限关系如下图:
关系图
主体、资源、权限相关的数据模型如下:
主体:用户id,账号,密码,…
资源:资源id,资源名称,访问地址,…
权限(分功能资源和数据资源):权限id,权限标识,权限名称,资源id,…
角色:角色id,角色名称,…
角色和权限关系:角色id,权限id,…
主体(用户)和角色的关系:用户id,角色id,…

主体(用户)、资源、权限关系图如下
关系图

通常企业开发中将资源和权限表何为一张权限表,如下:
资源:资源id,资源名称,访问地址,…
权限:权限id,权限标识,权限名称,资源id,…
合并为:
权限:权限id,权限标识,权限名称,资源名称,资源访问地址,…
修改后数据模型之间的关系如下图:
在这里插入图片描述

1.5 RBAC

如何实现授权?业界通常基于RBAC实现授权。

1.5.1 基于角色的访问控制

RBAC基于角色的访问控制(Role-Based Access Control)是按角色进行授权
比如:
	主体的角色为总经理,可以查询企业运营报表,查询员工工资信息等,访问控制流程如下图:

在这里插入图片描述
根据上图中的判断逻辑,授权代码可以表示如下:

if(主体.hasRole("总经理角色id")){
	//查询工资
}

如果上图中查询工资所需要的角色变化为总经理和部门经理,此时需要修改判断逻辑为“判断用户的角色是否是总经理或部门经理”,修改代码如下:

if(主体.hasRole("总经理角色id") || 主体.hasRole("部门经理角色id")){
	//查询工资
}
缺点:当修改角色的权限是需要修改授权的相关代码,系统的可扩展性差、健壮性差等。

1.5.2 基于资源的访问控制

RBAC基于资源的访问控制(Resource-Based Access Control)是按资源(或权限)进行授权。
比如:用户必须具有查询工资权限才可以查询员工工资信息等,访问控制流程如下:

在这里插入图片描述
根据上图中的判断,授权代码可以表示为:

if(主体.hasPermission("查询工资权限标识")){
	//查询工资
}
优点:系统设计时定义好查询工资的权限标识,即使查询工资所需要的角色变化为总经理和部门经理也不需要修改
授权代码,系统可扩展性强,健壮性强。

2 基于Session的认证方式

2.1认证流程(为了看起来方便,又抄了一遍)

基于Session认证方式的流程是,用户认证成功后,在服务端生成用户相关的数据保存在session(当前会话),而发
给客户端的session_id存到cookie中,这样用客户端请求时带上session_id就可以验证服务器端是否存在session
数据,以此完成用户的合法校验。当用户推出系统或session过期销毁时,客户端的sessi_id也就无效了。
	下图是session认证方法的流程图

在这里插入图片描述

基于session的认证机制由Servlet规范定制,Servlet容器已实现,用户通过HttpSession的操作方法即可实现,
如下是HttpSession相关的操作API
方法含义
HttpSession getSession(Boolean create)获取当前HttpSession对象
void setAttribute(String name,Object value)想session中存放对象
Object getAttribute(String name)从session中获取对象
void removeAttrbute(String name)一移除session中对象
void invalidate()是HttpSession失效
略…

2.2 创建工程(springMvc,如需SpringBoot请下翻)

本案例工程使用maven进行构建,使用SpringMVC、Servlet3.0实现。

2.2.1 创建maven工程

创建maven工程security-springMvc,工程结构如下

在这里插入图片描述

使用Tomcat7-maven-plugin插件来运行工程
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.webinar.sercurity</groupId>
    <artifactId>sercurity-springMvc</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.20</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>sercurity-springMvc</finalName>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <version>2.2</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <configuration>
                        <encoding>utf-8</encoding>
                        <useDefaultDelimiters>true</useDefaultDelimiters>
                        <resources>
                            <resource>
                                <directory>src/main/resources</directory>
                                <filtering>true</filtering>
                                <includes>
                                    <include>
                                        **/*
                                    </include>
                                </includes>
                            </resource>
                            <resource>
                                <directory>src/main/java</directory>
                                <includes>
                                    <include>
                                        **/*.xml
                                    </include>
                                </includes>
                            </resource>
                        </resources>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

2.2.2 spirng容器配置

在config包下定义ApplicationConfig,它对应web.xml中ContextLoaderListener的配置
package com.webinar.security.springMvc.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;

/**
 * @Author Webinar
 * @ClassName ApplicationConfig
 * @Description TODO
 * @Date 2022/10/8 21:45
 * @Version 1.0
 */
@Configuration
@ComponentScan(basePackages = "com.webinar.security.springMvc"
, excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)})
public class ApplicationConfig {
        //在此配置除了Controller的其他bean,比如:数据库连接池、失误管理器、业务bean等
}

2.2.3 servletContext配置

创建WEB-INF/view/login.jsp(当前登录页面为空白页面)
在这里插入图片描述
在这里插入图片描述

package com.webinar.security.springMvc.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

/**
 * @Author Webinar
 * @ClassName WebConfig
 * @Description TODO
 * @Date 2022/10/8 21:55
 * @Version 1.0
 */
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.webinar.security.springMvc",
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)})
public class WebConfig  implements WebMvcConfigurer {
        //视图解析器
        @Bean
    public InternalResourceViewResolver viewResolver(){
            InternalResourceViewResolver viewResolver=new InternalResourceViewResolver();
            //配置视图前缀
            viewResolver.setPrefix("/WEB-INF/view/");
            //配置视图后缀
            viewResolver.setSuffix(".jsp");
            return viewResolver;
        }

}

2.2.4 加载Spring容器

在init包下定义Spring容器初始化类SpringApplicationInitializer,此类实现WebApplicationInitializer
接口,Spring容器启动是加载WebApplicationInitializer接口的所有实现类。

在这里插入图片描述

package com.webinar.security.springMvc.init;

import com.webinar.security.springMvc.config.ApplicationConfig;
import com.webinar.security.springMvc.config.WebConfig;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

/**
 * @Author Webinar
 * @ClassName SpringApplicationInitializer
 * @Description TODO
 * @Date 2022/10/8 22:05
 * @Version 1.0
 */
public class SpringApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    //Spring容器  相当于加载applicationContext.xml
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{ApplicationConfig.class};
    }
    //servletContext 相当于加载springMvc,xml
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }
    //url-mapping
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

2.3 实现认证功能

2.3.1 认证页面

在webapp/WEB-INF/view下定义认证页面login.jsp,本案例只是测试认证流程,页面没有添加css样式,页面实现
可填入用户名,密码,出发登录将提交表单信息至/login,内容如下:
<%--
  Created by IntelliJ IDEA.
  User: Webinar
  Date: 2022/10/8
  Time: 22:00
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>用户登录</title>
</head>
<body>
    <form action="login" method="post">
        用户名:<input type="text" name="username"><br>&nbsp;&nbsp;&nbsp;码:<input type="password" name="password"> <br>
        <input type="submit" value="登录">
    </form>
</body>
</html>

在WebConfig中新增如下配置,将/直接导向login.jsp页面:

在这里插入图片描述

@Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("login");
    }

配置Mven启动项
在这里插入图片描述

启动过程可能会出现报错,原因是Tomcat7版本太低 不支持lombok 这些先不管 点击下面第一个红款为启动地址

在这里插入图片描述

点击地址后会弹出奇丑无比的登录页面

在这里插入图片描述

2.3.2 认证接口

创建service包以及AuthenticationService接口 以及Impl包和AuthenticationServiceImpl实现类 用于认证login服务 和model包 创建 userDto和AuthenticationRrquest类 用于接收参数和返回数据
1、service
在这里插入图片描述

package com.webinar.security.springMvc.service;

import com.webinar.security.springMvc.model.AuthenticationRrquest;
import com.webinar.security.springMvc.model.UserDto;

/**
 * @Author Webinar
 * @ClassName AuthenticationService
 * @Description TODO
 * @Date 2022/10/8 22:25
 * @Version 1.0
 */
public interface AuthenticationService {
    /**
     *
     * @param authenticationRrquest 用户认证请求,账号密码等等
     * @return 认证成功的用户信息
     */
    UserDto authentication(AuthenticationRrquest authenticationRrquest);
}

package com.webinar.security.springMvc.service.Impl;

import com.webinar.security.springMvc.model.AuthenticationRrquest;
import com.webinar.security.springMvc.model.UserDto;
import com.webinar.security.springMvc.service.AuthenticationService;
import org.springframework.stereotype.Service;

/**
 * @Author Webinar
 * @ClassName AuthenticationServiceImpl
 * @Description TODO
 * @Date 2022/10/8 22:31
 * @Version 1.0
 */
@Service
public class AuthenticationServiceImpl implements AuthenticationService {
    /**
     * 校验用户认证身份信息
     * @param authenticationRrquest 用户认证请求,账号密码等等
     * @return
     */
    @Override
    public UserDto authentication(AuthenticationRrquest authenticationRrquest) {
        return null;
    }
}

2、model
在这里插入图片描述

package com.webinar.security.springMvc.model;

import lombok.Data;

/**
 * @Author Webinar
 * @ClassName UserDto
 * @Description TODO
 * @Date 2022/10/8 22:28
 * @Version 1.0
 */
@Data
@AllArgsConstructor
public class UserDto {
    //用户认证信息
    private String id;
    private String username;
    private String password;
    private String fullName;
    private String mobile;
}

package com.webinar.security.springMvc.model;

import lombok.Data;

/**
 * @Author Webinar
 * @ClassName AuthenticationRrquest
 * @Description TODO
 * @Date 2022/10/8 22:28
 * @Version 1.0
 */
@Data
public class AuthenticationRrquest {
    //认证请求参数
    /**
     * 用户名
     */
    private String username;
    /**
     * 密码
     */
    private String password;
}

创建包以及实现类后 编写认证login服务代码

用户进入认证页面,输入账号密码,点击登录,请求/login进行身份认证。
(1)定义认证接口,此接口用于对传来的用户名账号密码进行校验,若成功则返回该用户的详细信息,否则抛出错误异常(如下)(AuthenticationServiceImpl实现类中):
package com.webinar.security.springMvc.service.Impl;

import com.webinar.security.springMvc.model.AuthenticationRrquest;
import com.webinar.security.springMvc.model.UserDto;
import com.webinar.security.springMvc.service.AuthenticationService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * @Author Webinar
 * @ClassName AuthenticationServiceImpl
 * @Description TODO
 * @Date 2022/10/8 22:31
 * @Version 1.0
 */
@Service
public class AuthenticationServiceImpl implements AuthenticationService {
    /**
     * 校验用户认证身份信息
     * @param authenticationRrquest 用户认证请求,账号密码等等
     * @return
     */
    @Override
    public UserDto authentication(AuthenticationRrquest authenticationRrquest) {
        //1、校验参数是否为空
        if(authenticationRrquest==null ||
                authenticationRrquest.getUsername().isEmpty() ||
                authenticationRrquest.getUsername().isEmpty()){
            throw new RuntimeException("账号或密码为空");
        }
        //2、根据账号去查询数据库(由于这里是测试程序,则采用模拟数据)
        UserDto user = getUserDtoByUserName(authenticationRrquest.getUsername());
        //校验账户是否存在
        if(user == null){
            throw new RuntimeException("账户不存在");
        }
        //校验密码
        if(!authenticationRrquest.getPassword().equals(user.getPassword())){
            throw new RuntimeException("账号或密码错误");
        }
        //认证通过 返回用户的身份信息
        return user;
    }
    //根据账号查询用户信息
    private UserDto getUserDtoByUserName(String userName){
        return userMap().get(userName);
    }

    //用户信息
    private Map<String,UserDto> userMap(){
        Map<String,UserDto> stringUserDtoMap=new HashMap<>();
        stringUserDtoMap.put("zhangsan",new UserDto("1010","zhangsan","123","1231321","123"));
        stringUserDtoMap.put("lisi",new UserDto("1011","lisi","456","42456456","213"));
        return stringUserDtoMap;
    }
}

创建Controller包 创建LoginController
在这里插入图片描述

package com.webinar.security.springMvc.Controller;

import com.webinar.security.springMvc.model.AuthenticationRrquest;
import com.webinar.security.springMvc.model.UserDto;
import com.webinar.security.springMvc.service.AuthenticationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author Webinar
 * @ClassName LoginController
 * @Description TODO
 * @Date 2022/10/8 22:49
 * @Version 1.0
 */
@RestController
public class LoginController {

    @Autowired
    AuthenticationService authenticationService;

    @RequestMapping(value = "/login",produces = "text/plain;charset=utf-8")
    public String login(AuthenticationRrquest authenticationRrquest){
        UserDto authentication = authenticationService.authentication(authenticationRrquest);
        return authentication.toString() +"----"+"登录成功!";
    }
}

效果
在这里插入图片描述
在这里插入图片描述

总结:以上是实现登录的全过程,而Security也是利用这样的流程来进行封装的,所需需要了解一下。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值