五、Spring Security权限控制(优医健康)

1 认证和授权概念

前面我们已经完成了编易健康后台管理系统的部分功能,例如检查项管理、检查组管理、套餐管理、预约设置等。接下来我们需要思考2个问题:
问题1:在生产环境下我们如果不登录后台系统就可以完成这些功能操作吗?
答案显然是否定的,要操作这些功能必须首先登录到系统才可以。
问题2:是不是所有用户,只要登录成功就都可以操作所有功能呢?
答案是否定的,并不是所有的用户都可以操作这些功能。不同的用户可能拥有不同的权限,这就需要进行授权了。
认证:系统提供的用于识别用户身份的功能,通常提供用户名和密码进行登录其实就是在进行认证,认证的目的是让系统知道你是谁。
授权:用户认证成功后,需要为用户授权,其实就是指定当前用户可以操作哪些功能。
本章节就是要对后台系统进行权限控制,其本质就是对用户进行认证和授权。

2权限模块数据模型

前面已经分析了认证和授权的概念,要实现最终的权限控制,需要有一套表结构支撑:
用户表t_user、权限表t_permission、角色表t_role、菜单表t_menu、用户角色关系表 t_user_role、角色权限关系表t_role_permission、角色菜单关系表t_role_menu。
表之间关系如下图:
在这里插入图片描述
通过上图可以看到,权限模块共涉及到7张表。在这7张表中,角色表起到了至关重要的作用,其处于核心位置,因为用户、权限、菜单都和角色是多对多关系。

接下来我们可以分析一下在认证和授权过程中分别会使用到哪些表:

认证过程:只需要用户表就可以了,在用户登录时可以查询用户表t_user进行校验,判断 用户输入的用户名和密码是否正确。

授权过程:用户必须完成认证之后才可以进行授权,首先可以根据用户查询其角色,再根据角色查询对应的菜单,这样就确定了用户能够看到哪些菜单。然后再根据用户的角 色查询对应的权限,这样就确定了用户拥有哪些权限。所以授权过程会用到上面7张表。

3 Spring Security简介

Spring Security是 Spring提供的安全认证服务的框架。 使用Spring Security可以帮助我 们来简化认证和授权的过程。官网:https://spring.io/projects/spring-security
在这里插入图片描述
对应的maven坐标:

<dependency>   
<groupId>org.springframework.security</groupId>
<artifactId>spring‐security‐web</artifactId>   
<version>5.0.5.RELEASE</version> 
</dependency> 
<dependency>   
<groupId>org.springframework.security</groupId>   
<artifactId>spring‐security‐config</artifactId>   
<version>5.0.5.RELEASE</version>
</dependency>

常用的权限框架除了Spring Security,还有Apache的shiro框架。

4 Spring Security入门案例

1 工程搭建

创建maven工程,打包方式为war,为了方便起见我们可以让入门案例工程依赖 health_interface,这样相关的依赖都继承过来了。
在pom.xml中:

<?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">
    <parent>
        <artifactId>health_parent</artifactId>
        <groupId>com.qf</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring_security</artifactId>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>com.qf</groupId>
            <artifactId>health_interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <configuration>
                    <!-- 指定端口 -->
                    <port>85</port>
                    <!-- 请求路径 -->
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>


提供index.html页面,内容为hello Spring Security!!

2 配置web.xml

在web.xml中主要配置SpringMVC的DispatcherServlet和用于整合第三方框架的 DelegatingFilterProxy,用于整合Spring Security。

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>
    <filter>
        <!--
DelegatingFilterProxy:用于整合第三方框架,整合Spring Security时过滤器的名称必须为
springSecurityFilterChain,否则会抛出NoSuchBeanDefinitionException异常
        -->
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <!--设置spring容器filter的bean id,如果不设置会找filter-name -->
            <param-name>targetBeanName</param-name>
            <param-value>springSecurityFilterChain</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 指定加载的配置文件 ,通过参数contextConfigLocation加载 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-security.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</web-app>

3 配置spring-security.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                  http://www.springframework.org/schema/beans/spring-beans.xsd
                  http://www.springframework.org/schema/mvc
                  http://www.springframework.org/schema/mvc/spring-mvc.xsd
                  http://code.alibabatech.com/schema/dubbo
                  http://code.alibabatech.com/schema/dubbo/dubbo.xsd
                  http://www.springframework.org/schema/context
                  http://www.springframework.org/schema/context/spring-context.xsd
                     http://www.springframework.org/schema/security
                     http://www.springframework.org/schema/security/spring-security.xsd">
    <!--
        http:用于定义相关权限控制
        auto-config:是否自动配置
                        设置为true时框架会提供默认的一些配置,例如提供默认的登录页面、登出处理等
                        设置为false时需要显示提供登录表单配置,否则会报错
        use-expressions:用于指定intercept-url中的access属性是否使用表达式
    -->
    <security:http auto-config="true" use-expressions="true">
        <!--
            pattern:描述拦截规则
            asscess:指定所需的访问角色或者访问权限
        -->
        <security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"></security:intercept-url>
    </security:http>
    <!--配置认证管理器-->
    <security:authentication-manager>
        <!--配置认证提供者-->
        <security:authentication-provider>
            <security:user-service>
                <!--配置一个具体的用户,后期需要从数据库查询用户{noop}:表示当前使用的密码为明文-->
                <security:user name="admin" password="{noop}1234" authorities="ROLE_ADMIN"/>
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>
</beans>

4、测试

在这里插入图片描述
问题:这个登录页面是谁写的?

5、源码分析

DelegatingFilterProxy

我们在web.xml中配置了一个名称为springSecurityFilterChain的过滤器DelegatingFilterProxy:
在这里插入图片描述
我们点击进入DelegatingFilterProxy的源码,执行过滤器时会调用这个类的doFiler方法:
在这里插入图片描述
然后我们再进入initDelegate方法里面打断点,观察它的具体的初始化流程:
在这里插入图片描述

  1. 这里我们在web.xml里面配置的filter-name或param-name必须是springSecurityFilterChain
  2. DelegatingFilterProxy通过springSecurityFilterChain这个名称,得到了一个FilterChainProxy过滤器。
    问题:好了,那现在疑问又来了,什么是FilterChainProxy?
FilterChainProxy

查看源码我们发现DelegatingFilterProxy会把请求交给FilterChainProxy去处理:
在这里插入图片描述
我们进入FilterChainProxy源码就知道了,还是找到doFilter方法:
在这里插入图片描述
在这里插入图片描述
总结:

  1. DelegatingFilterProxy会把请求交给FilterChainProxy去处理
  2. 当请求到达 FilterChainProxy 之后,FilterChainProxy 会根据请求的路径,将请求转发到不同的 Spring Security Filters 上面去,不同的 Spring Security Filters 对应了不同的过滤器,也就是不同的请求将经过不同的过滤器。

6 SpringSecurity常用过滤器介绍

常用的过滤器有15个,分别如下:
1.org.springframework.security.web.context.SecurityContextPersistenceFilter
首当其冲的一个过滤器,作用之重要,自不必多言。
SecurityContextPersistenceFilter主要是使用SecurityContextRepository在session中保存或更新一个 SecurityContext,并将SecurityContext给以后的过滤器使用,来为后续filter建立所需的上下文。 SecurityContext中存储了当前用户的认证以及权限信息。简单的说就是初始化一个类似于springIOC的容器。
2.org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter
此过滤器用于集成SecurityContext到Spring异步执行机制中的WebAsyncManager如果springSecurity要整合到spring工程里面去,必须使用这个过滤器。
3.org.springframework.security.web.header.HeaderWriterFilter
向请求的Header中添加相应的信息,可在http标签内部使用security:headers来控制
4.org.springframework.security.web.csrf.CsrfFilter
csrf又称跨域请求伪造,SpringSecurity会对所有post请求验证是否包含系统生成的csrf的token信息,如果不包含,则报错。起到防止csrf攻击的效果。
5.org.springframework.security.web.authentication.logout.LogoutFilter
匹配URL为/logout的请求,实现用户退出,清除认证信息。
6.org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
认证操作全靠这个过滤器,默认匹配URL为/login且必须为POST请求。
7.org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter
如果没有在配置文件中指定认证页面,则由该过滤器生成一个默认认证页面。
8.org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter
由此过滤器可以生产一个默认的退出登录页面
9.org.springframework.security.web.authentication.www.BasicAuthenticationFilter
此过滤器会自动解析HTTP请求中头部名字为Authentication,且以Basic开头的头信息。简而言之就是解析Http请求头信息。
10.org.springframework.security.web.savedrequest.RequestCacheAwareFilter
通过HttpSessionRequestCache内部维护了一个RequestCache,用于缓存HttpServletRequest
11.org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
针对ServletRequest进行了一次包装,使得request具有更加丰富的API
12.org.springframework.security.web.authentication.AnonymousAuthenticationFilter
当SecurityContextHolder中认证信息为空,则会创建一个匿名用户存入到SecurityContextHolder中。 springsecurity为了兼容未登录的访问,也走了一套认证流程,只不过是一个匿名的身份。
13.org.springframework.security.web.session.SessionManagementFilterSecurityContextRepository限制同一 用户开启多个会话的数量
14.org.springframework.security.web.access.ExceptionTranslationFilter
异常转换过滤器位于整个springSecurityFilterChain的后方,用来转换整个链路中出现的异常。简单说就是进行 springSecurity中的异常处理。
15. org.springframework.security.web.access.intercept.FilterSecurityInterceptor
获取所配置资源访问的授权信息,根据SecurityContextHolder中存储的用户信息来决定其是否有权限。简单的说就是 授权。

7 对入门案例进行改进(静态数据)

前面我们已经完成了Spring Security的入门案例,通过入门案例我们可以看到,Spring Security将我们项目中的所有资源都保护了起来,要访问这些资源必须要完成认证而且需 要具有ROLE_ADMIN角色。
但是入门案例中的使用方法离我们真实生产环境还差很远,还存在如下一些问题:
1、项目中我们将所有的资源(所有请求URL)都保护起来,实际环境下往往有一些资源 不需要认证也可以访问,也就是可以匿名访问。
2、登录页面是由框架生成的,而我们的项目往往会使用自己的登录页面。
3、直接将用户名和密码配置在了配置文件中,而真实生产环境下的用户名和密码往往保 存在数据库中。
4、在配置文件中配置的密码使用明文,这非常不安全,而真实生产环境下密码需要进行加密。
本章节需要对这些问题进行改进。

在这里插入图片描述

修改spring-security.xml文件,关闭CsrfFilter过滤器,解决跨域

启动测试
使用http://localhost:8085/访问,此时就能访问自定义的登录页面。

8、登录调用服务层验证使用(模拟sql)

在spring-security.xml中:
在这里插入图片描述
在这里插入图片描述
实现类:UserServiceImpl
在这里插入图片描述
启动测试:
当登录用户为admin时
在这里插入图片描述
当用户为test时,为403
在这里插入图片描述
账号或者密码不对的时候,会自动返回到login.html
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

杵意

谢谢金主打赏呀!!

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

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

打赏作者

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

抵扣说明:

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

余额充值