Spring Security权限管理框架

每篇一句

江月何年初照人,江畔何人初见月 —— 唐 · 张若虚《春江花月夜》


认证和授权概念

  • 认证:系统提供的用于识别用户身份的功能,通常提供用户名和密码进行登录其实就是在进行认证,认证的目的是让系统知道你是谁。
  • 授权:用户认证成功后,需要为用户授权,其实就是指定当前用户可以操作哪些功能。

RBAC权限模块数据模型

前面已经分析了认证和授权的概念,要实现最终的权限控制,需要有一套表结构支撑:

用户表t_user、 角色表t_role、权限表t_permission、菜单表t_menu

用户角色关系表t_user_role、角色权限关系表t_role_permission、角色菜单关系表t_role_menu。

表之间关系如下图:

在这里插入图片描述
在这里插入图片描述
通过上图可以看到,权限模块共涉及到7张表。在这7张表中,角色表起到了至关重要的作用,其处于核心位置,因为用户、权限、菜单都和角色是多对多关系。

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

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

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


Spring Security

简介

Spring Security是 Spring提供的安全认证服务的框架。 使用Spring Security可以帮助我们来简化认证和授权的过程。官网:https://spring.io/projects/spring-security 常用的权限框架除了Spring Security,还有Apache的shiro框架。

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>

快速入门

  1. 创建Maven工程 springsecurity_demo,导入坐标

这一步就略过, maven坐标简介中已经介绍了, 因为整合spring,所以记得导入spring相关坐标

  1. 创建spring-security.xml(1.自动配置 2.配置授权管理器)
 <?xml version="1.0" encoding="UTF-8"?>
<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: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://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">
 <!--配置需要权限才能访问的资源,其实就是拦截规则:
        auto-config: true 自动配置
        use-expressions: false 不使用表达式
    -->
    <security:http auto-config="true" use-expressions="false">
        <!--配置拦截的路径:
            pattern: 拦截的路径规则
            access: 需要什么角色才能访问
        -->
        <security:intercept-url pattern="/**" access="ROLE_ADMIN"/>
    </security:http>
    <!--配置认证管理器-->
    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
                <!--配置账号密码,以及该账户的角色信息
                name: 用户名
                password: 密码 {noop}表示不加密方式
                authorities: 该账户赋予的角色
                -->
                <security:user name="admin" authorities="ROLE_ADMIN" password="{noop}admin"/>
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>
</beans>

在spring-security.xml中主要配置Spring Security的拦截规则和认证管理器

  1. 配置web.xml(前端控制器, SpringSecurity相关的过滤器)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--配置spring核心控制器-->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <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>dispatcherServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

    <!--配置代理过滤器: -->
    <filter>
        <!--DelegatingFilterProxy用于整合第三方框架整合Spring Security时过滤器的名称必须为springSecurityFilterChain,
        否则会抛出NoSuchBeanDefinitionException异常-->
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

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

  1. 测试

配置完以上内容之后,在Tomcat上启动程序, 访问任何页面都会自动跳转到Spring Security框架生成的登录页面上,只有登录了才能访问程序的其他资源. 这个登录页面是框架底层过滤器生成的.
在这里插入图片描述


Spring Security进阶

前面我们已经完成了Spring Security的入门案例,通过入门案例我们可以看到,Spring Security将我们项目中的所有资源都保护了起来,要访问这些资源必须要完成认证而且需要具有ROLE_ADMIN角色。

但是快速入门中的使用方法离我们真实生产环境还差很远,还存在如下一些问题:

1、项目中我们将所有的资源(所有请求URL)都保护起来,实际环境下往往有一些资源不需要认证也可以访问,也就是可以匿名访问。

2、登录页面是由框架生成的,而我们的项目往往会使用自己的登录页面。毕竟自动生成的登录页面太简介了太丑了

配置可匿名访问的资源 : 所谓匿名访问资源,就是不需要任何权限就能访问到的资源
第一步:在项目中创建js、css目录并在两个目录下提供任意一些测试文件,再创建登录和注册页面
第二步:在spring-security.xml文件中增加配置,指定哪些资源可以匿名访问

<!--配置可以匿名访问的资源,这个必须在拦截全部(pattern="/**" )的前面配置
       security="none"  就是可以通过匿名访问
       patern: 指定可以匿名访问的资源路径,  /** 表示后代的全部子路径
    -->
<security:http pattern="/js/**" security="none"></security:http>
<security:http pattern="/img/**" security="none"></security:http>
<security:http pattern="/css/**" security="none"></security:http>
<security:http pattern="/login.html" security="none"></security:http>
<security:http pattern="/regist.html" security="none"></security:http>

<!--配置需要权限才能访问的资源-->
<security:http auto-config="true" use-expressions="false">
	   <security:intercept-url pattern="/**" access="ROLE_ADMIN"/>
</security:http>

使用指定的登录页面 : 就是不使用框架生成的登录页面
第一步:提供login.html作为项目的自定义登录页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<h1>登录页面</h1>
<form action="/login.do" method="post">
    username:<input type="text" name="username"><br>
    password:<input type="password" name="password"><br>
    <input type="submit" value="submit">
</form>
</body>
</html>

第二步:修改spring-security.xml文件,指定login.html页面可以匿名访问

  <security:http security="none" pattern="/login.html" />

第三步:修改spring-security.xml文件,加入表单登录信息的配置 , 并关闭CsrfFilter过滤器

  <security:http auto-config="true" use-expressions="false">
        <security:intercept-url pattern="/**" access="ROLE_ADMIN"/>
        <!--配置自定义的登录页面
            username-parameter:登录的input中的name属性值,默认是username
            password-parameter:登录的input中的password属性值,默认是password
            login-processing-url:登录请求
            authentication-failure-url:登录失败页面
            authentication-success-forward-url:指定登录成功跳转的页面
        -->
        <security:form-login
                login-page="/login.html"
                username-parameter="username"
                password-parameter="password"
                login-processing-url="/login.do"
                authentication-failure-url="/login.html"
                authentication-success-forward-url="/index.jsp"
        ></security:form-login>
        <!--
             csrf:对应CsrfFilter过滤器
             disabled:是否启用CsrfFilter过滤器,如果使用自定义登录页面需要关闭此项,否则登录操作会被禁用(403,拒绝访问
           -->
        <security:csrf disabled="true"></security:csrf>
    </security:http>

至此,重启Tomcat服务, 可以不登录就访问匿名资源, 如果访问一个需要权限的页面,就会自动跳转到我们自己的登录页面,而不是框架生成的登录页面了,登录成功之后,就能访问需要权限的资源了 (登录账户在认证管理器中配置了,我配的都是admin)
在这里插入图片描述
为了防止大家看混来,因为总是在配置文件总加东西 ,可能你不清楚加到哪里,我把整个配置文件一起发一下,你写的时候 可以参考下: 注释就不写了,因为上面已经写了非常清楚的注释了,使用不重复写了

<?xml version="1.0" encoding="UTF-8"?>
<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: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://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">

    <!--配置可以匿名访问的资源-->
    <security:http security="none" pattern="/login.html"/>
    <security:http security="none" pattern="/css/**"/>
    <security:http security="none" pattern="/js/**"/>
    
	<!--配置需要权限才能访问的资源-->
    <security:http auto-config="true" use-expressions="false">
   	     
        <security:intercept-url pattern="/**" access="ROLE_ADMIN"/>

        <!--配置自定义的登录页面-->
        <security:form-login
                login-page="/login.html"
                username-parameter="username"
                password-parameter="password"
                login-processing-url="/login.do"
                authentication-failure-url="/login.html"
                default-target-url="/index.jsp"
                authentication-success-forward-url="/index.jsp"
        ></security:form-login>
 
        <security:csrf disabled="true"></security:csrf>
        
    </security:http>

    <!--配置认证管理器-->
    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
            	<!--账户是admim , 密码也是admin-->
                <security:user name="admin" authorities="ROLE_ADMIN" password="{noop}admin"/>
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>
</beans>

Spring Security高级

前面我们在Spring Security进阶中, 配置了匿名访问资源和指定了登录页面.但是Spring Security进阶中还存在如下一些问题:

  1. 直接将用户名和密码配置在了配置文件中,而真实生产环境下的用户名和密码往往保存在数据库中。
  2. 在配置文件中配置的密码使用明文,这非常不安全,而真实生产环境下密码需要进行加密。

从数据库中查询用户信息 :

如果我们要从数据库动态查询用户信息,就必须按照spring security框架的要求提供一个实现UserDetailsService接口的实现类,并按照框架的要求进行配置即可。框架会自动调用实现类中的方法并自动进行密码校验。

第一步: 创建一个类实现UserDetailsService接口

框架实现认证授权的过程分析:
1.页面发请求,经过框架的登录请求,获取到用户输入的密码
2.security框架调用loadUserByUsername 获取数据库的中的密码
3.security框架 将 用户输入的密码和 数据库查询的密码进行验证(框架内部实现的)不需要我们去写校验过程

package com.zuoyueer.security;

import com.zuoyueer.pojo.User;
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.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Zuoyueer
 * Date: 2019/12/14
 * Time: 11:15
 * @projectName health_parent
 * @description: 自定义授权类
 */
public class MySpringSecurityService implements UserDetailsService {

    /**
     * @param username 客户端传递来的用户名
     * @return UserDetails的实现类org.springframework.security.core.userdetails.User
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //1. 根据用户查询查询数据库,获得User对象
        User dbUser = findUserFromDbByUsername(username);

        //用户对象判断,如果是空, 返回空
        if (dbUser == null) {
            return null;
        }

        //如果不是空,把用户名,数据库查询出来的密码和访问权限, 创建UserDetails对象返回

        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        //给该用户设置权限,这里写死了,实际上是从数据库中查询出来的
        grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        grantedAuthorities.add(new SimpleGrantedAuthority("add"));
        //3个参数,一个是用户名,二是数据库密码,三是该用户的权限
        //"{noop}"+dbUser.getPassword()的意思对密码不加密,{noop}必须加上,不然会登录会报错,因为框架默认必须对密码加密
        return new org.springframework.security.core.userdetails.User(username, "{noop}"+dbUser.getPassword(), grantedAuthorities);
    }

    //为了简化和本文不相关的代码,我们模拟从数据库查询用户信息
    private User findUserFromDbByUsername(String username) {
        if ("admin".equals(username)) {
            //pojo的User类
            User user = new User();
            user.setUsername(username);
            user.setPassword("123456");
            return user;
        }
        //查询不到返回空
        return null;
    }
}

第二步: 在spring-security.xml进行配置


    <!--注册MySpringSecurtiyService-->
    <bean id="mySpringSecurityService" class="com.zuoyueer.security.MySpringSecurityService"/>

    <!--删除原来的认证管理器, -配置为自定义认证授权类,user-service-ref属性就是指定UserDetailsService实现类-->
    <security:authentication-manager>
        <security:authentication-provider user-service-ref="mySpringSecurityService"/>
    </security:authentication-manager>

至此我们已经实现了调用数据库查询用户信息,并登录,而不是在配置文件中写死登录信息

对密码进行加密

可能你已经注意到了: 在上面实现类中,返回的return new org.springframework.security.core.userdetails.User(username, "{noop}"+dbUser.getPassword(), grantedAuthorities);其中我们通过拼接{noop}指定了不对密码进行加密.但是这种是不推荐的,所以我们接下来要对密码进行加密,官方推荐使用bcrypt加密技术来对密码进行加密,

第一步: 修改配置文件, 配置指定加密方式

  <!--注册MySpringSecurtiyService-->
    <bean id="mySpringSecurityService" class="com.zuoyueer.security.MySpringSecurityService"/>

    <!--注册bcrypt密码加密对象-->
    <bean id="bCryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

    <!--认证管理器 -配置为自定义认证授权类-->
    <security:authentication-manager>
        <security:authentication-provider user-service-ref="mySpringSecurityService">
            <!--指定密码加密策略-->
            <security:password-encoder ref="bCryptPasswordEncoder"/>
        </security:authentication-provider>
    </security:authentication-manager>

第二步: 修改实现类,对密码进行加密

package com.zuoyueer.security;

import com.zuoyueer.pojo.User;
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.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Zuoyueer
 * Date: 2019/12/14
 * Time: 11:15
 * @projectName health_parent
 * @description: 自定义授权类
 */
public class MySpringSecurityService implements UserDetailsService {

    //注入依赖
    @Autowired
    private BCryptPasswordEncoder encoder;

    /**
     * @param username 客户端传递来的用户名
     * @return UserDetails的实现类org.springframework.security.core.userdetails.User
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户查询查询数据库,获得User对象
        User dbUser = findUserFromDbByUsername(username);

        //用户对象判断,如果是空, 返回空
        if (dbUser == null) {
            return null;
        }
        //如果不是空,把用户名,数据库查询出来的密码和访问权限, 创建UserDetails对象返回
        
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        //给该用户设置权限,这里写死了,实际上是从数据库中查询出来的
        grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        grantedAuthorities.add(new SimpleGrantedAuthority("add"));
        //return new org.springframework.security.core.userdetails.User(username, "${noop}"+dbUser.getPassword(), grantedAuthorities);
        return new org.springframework.security.core.userdetails.User(username, dbUser.getPassword(), grantedAuthorities);
    }

    //为了简化和本文不相关的代码,我们模拟从数据库查询用户信息
    private User findUserFromDbByUsername(String username) {
        if ("admin".equals(username)) {
            //pojo的User类
            User user = new User();
            user.setUsername(username);
            //user.setPassword("123456");
            //对密码进行加密
            user.setPassword(encoder.encode("123456"));
            return user;
        }
        //查询不到返回空
        return null;
    }
}

至此我们已经实现了调用数据库查询用户信息 因为实际上数据库的密码是加密的,所以我们要加密对登录密码进行加密, 校验过程是框架实现的,我们只需要把加密后的密码以参数的形式传递给框架就可以了


Spring Security配置文件方式角色权限控制

配置文件的方式分两种情况,一种是使用表达式,一种是不使用表达式 :

use-expressions="false"的时候,配置权限access都不能使用表达式,否则启动报错

  • 不使用表达式,那么角色名前必须加上 ROLE_,如下所示access="ROLE_add", 如果直接写access="add", 会启动报错
		<!--不使用表达式:  拥有add权限就可以访问b.html页面-->
        <security:intercept-url pattern="/b.html"  access="ROLE_add" />
  • 登录了就能访问,不需要特殊角色权限, 直接使用access=""表示
		<!--不使用表达式: 只要认证通过就可以访问,不需要具体的角色权限-->
        <security:intercept-url pattern="/index.jsp"  access="" />
        <security:intercept-url pattern="/a.html"  access="" />

同理当use-expressions="true"的时候,配置权限access都必须使用表达式,否则启动报错

  • 表达式的写法请注意: hasAuthority()参数是角色名的字符串,如果写成access="hasAuthority(add)"就错了
  • 之所以可以不加ROLE_是因为使用了表达式,没写的时候框架会自动帮我们补上前缀ROLE_
	    <!--使用表达式:  拥有add权限就可以访问b.html页面-->
        <security:intercept-url pattern="/b.html"  access="hasAuthority('add')" />
  • 登录了就能访问,不需要特殊角色权限, 使用access="isAuthenticated()", 他的效果和不使用表达式的access=""一样
 		<!--使用表达式: 只要认证通过就可以访问,不需要具体的角色权限-->
        <security:intercept-url pattern="/index.jsp"  access="isAuthenticated()" />
        <security:intercept-url pattern="/a.html"  access="isAuthenticated()" />

另外还需要特别注意的是,配置不同路径的权限,需要考虑配置先后的问题, 如果顺序配置错误,会导致配置无效

  • 看下面的配置,pattern="/**"的路径范围包含pattern="/b.html" 并且范围大的配置在前面,这会导致,拥有ROLE_ADMIN角色权限的用户也能访问到b.html,也就是说范围小的配置无效了
		<!--拥有ROLE_ADMIN角色就可以访问全部资源-->
  		<security:intercept-url pattern="/**" access="ROLE_ADMIN"/>
 		<!--拥有add权限才可以访问b.html页面-->
        <security:intercept-url pattern="/b.html"  access="ROLE_add" />
  • 正确的配置方式,应该是如下所示,范围小的配置在前面,这样,拥有ROLE_ADMIN角色权限的用户不能访问到b.html,只有拥有add角色权限的用户才能访问b.html
		<!--拥有add权限才可以访问b.html页面-->
        <security:intercept-url pattern="/b.html"  access="ROLE_add" />
        <!--拥有ROLE_ADMIN角色就可以访问全部资源-->
  		<security:intercept-url pattern="/**" access="ROLE_ADMIN"/>
  • 还有一点需要补充,就是pattern的值必须写绝对路径,也就是前面必须加/,比如pattern="b.html"这样写是错误的,写的不会报错,但是配置无效.

至此使用配置文件的方式配置进行角色的权限控制已经说完了, 下面我写出一个完整的配置,以供参考:

<?xml version="1.0" encoding="UTF-8"?>
<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: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://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">
       
    <!--配置可以匿名访问的资源,这个必须在拦截全部(pattern="/**" )的前面配置
       security="none"  就是可以通过匿名访问
       patern: 指定可以匿名访问的资源路径,  ** 表示后代的全部子路径
    -->
    <security:http security="none" pattern="/login.html"/>
    <security:http security="none" pattern="/css/**"/>
    <security:http security="none" pattern="/js/**"/>


    <!--配置需要权限才能访问的资源,其实就是拦截规则:
        http:用于定义相关权限控制
        auto-config:是否自动配置
                        设置为true时框架会提供默认的一些配置,例如提供默认的登录页面、登出处理等
                        设置为false时需要显示提供登录表单配置,否则会报错
        use-expressions: 用于指定intercept-url中的access属性是否使用表达式
                        false 不使用表达式;  true:使用表达式  :   (hasRole('ROLE_ADMIN')
    -->
    <security:http auto-config="true" use-expressions="false">
        <!--
             intercept-url:定义一个拦截规则
             pattern:对哪些url进行权限控制
             access:在请求对应的URL时需要什么权限,默认配置时它应该是一个以逗号分隔的角色列表,请求的用户只需拥有其中的一个角色就能成功访问对应的URL
             isAuthenticated():已经经过认证(不是匿名用户)
         -->

        <!--只要认证通过就可以访问-->
        <security:intercept-url pattern="/index.jsp"  access="" />
        <security:intercept-url pattern="/a.html"  access="" />
        
        <!--拥有add权限就可以访问b.html页面-->
        <security:intercept-url pattern="/b.html"  access="ROLE_add" />

        <!--拥有ROLE_ADMIN角色就可以访问c.html页面-->
        <security:intercept-url pattern="/c.html"  access="ROLE_ADMIN" />

        <!--拥有ROLE_ADMIN角色就可以访问d.html页面-->
        <security:intercept-url pattern="/d.html"  access="ROLE_ADMIN" />
        <!--大范围的路径,必须写在后面-->
        <security:intercept-url pattern="/**" access="ROLE_ADMIN"/>

        <!--form-login 配置自定义的登录页面
            username-parameter:登录的input中的name属性值,默认是username
            password-parameter:登录的input中的password属性值,默认是password
            login-processing-url:登录请求
            authentication-failure-url:登录失败页面
            authentication-success-forward-url:指定登录成功跳转的页面
        -->
        <security:form-login
                login-page="/login.html"
                username-parameter="username"
                password-parameter="password"
                login-processing-url="/login.do"
                authentication-failure-url="/login.html"
                default-target-url="/index.jsp"
                authentication-success-forward-url="/index.jsp"
        ></security:form-login>

        <!--配置登出,不配做的话要访问localhost:8080/logout才能退出登录
            配置之后: 访问localhost:8080/logout.do 才能退出登录
            logout-url: 退出登录的路径
            logout-success-url: 退出登录后,跳转的页面
            invalidate-session: 退出登录后,是否销毁session
        -->
        <security:logout logout-url="/logout.do" logout-success-url="/login.html" invalidate-session="true"/>

        <!--
             csrf:对应CsrfFilter过滤器
             disabled:是否启用CsrfFilter过滤器,如果使用自定义登录页面需要关闭此项,否则登录操作会被禁用(403,拒绝访问
           -->
        <security:csrf disabled="true"></security:csrf>
    </security:http>

    <!--注册MySpringSecurtiyService-->
    <bean id="mySpringSecurityService" class="com.zuoyueer.security.MySpringSecurityService"/>

    <!--注册bcrypt密码加密对象-->
    <bean id="bCryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

    <!-- authentication-manager:认证管理器,用于处理认证操作 -配置为自定义认证授权类-->
    <security:authentication-manager>
        <!-- authentication-provider:认证提供者,执行具体的认证逻辑
                    user-service-ref指定认证实现类 -->
        <security:authentication-provider user-service-ref="mySpringSecurityService">
            <!--指定密码加密策略-->
            <security:password-encoder ref="bCryptPasswordEncoder"/>
        </security:authentication-provider>
    </security:authentication-manager>
</beans>

使用注解方式配置角色权限控制

Spring Security除了可以在配置文件中配置权限校验规则,还可以使用注解方式控制类中方法的调用。例如Controller中的某个方法要求必须具有某个权限才可以访问,此时就可以使用Spring Security框架提供的注解方式进行控制。

第一步: 在spring-security.xml文件中配置组件扫描,用于扫描Controller

	<!--开启mvc的注解驱动-->
    <mvc:annotation-driven/>
     <!--开启注解包扫描-->
    <context:component-scan base-package="com.zuoyueer.controller"/>

第二步:在spring-security.xml文件中开启权限注解支持

 	<!--开启注解方式权限控制-->
 	<security:global-method-security pre-post-annotations="enabled" />

第三步:创建Controller类并在Controller的方法上加入注解进行权限控制

  • @PreAuthorize注解加在方法上,参数和配置文件方式的表达式写法一样, 有两种写法.
<!--配置文件中的 表达式写法: -->
		 <!--拥有add权限就可以访问b.html页面-->
        <security:intercept-url pattern="/b.html"  access="hasAuthority('add')" />
        <!--拥有ROLE_ADMIN角色就可以访问c.html页面-->
        <security:intercept-url pattern="/c.html"  access="hasRole('ROLE_ADMIN')" />
  • 必须使用表达式写法,不能不写表达式,否则会报这个异常 EL1008E: Property or field 'ROLE_ADMIN' cannot be found on object of type 'org.springframework.security.access.expression.method.MethodSecurityExpressionRoot' - maybe not public or not valid?
package com.zuoyueer.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author Zuoyueer
 * Date: 2019/12/15
 * Time: 15:10
 * @projectName health_parent
 * @description: 注解方式实现角色权限控制
 */
@Controller
@RequestMapping("/hello")
public class HelloController {
    @RequestMapping("/add")
    @PreAuthorize("hasAuthority('add')")
    public String addMethod(){
        System.out.println("拥有add角色权限的用户才能调用当前的方法...");
        return null;
    }

    @RequestMapping("/delete")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public String delete(){
        System.out.println("拥有delete角色权限的用户才能调用当前的方法...");
        return null;
    }

    /**
     * @PreAuthorize("ROLE_ADMIN")写是错误的,必须使用表达式,不然访问的时候会报错
     * @return
     */
    @RequestMapping("/delete2")
    @PreAuthorize("ROLE_ADMIN")
    public String delete2(){
        System.out.println("拥有delete角色权限的用户才能调用当前的方法2222...");
        return null;
    }

}

登录后,访问 http://localhost:85/hello/delete.do,在控制台会打印输出,说明访问成功,
访问http://localhost:85/hello/add.do,在控制台不会输出, 因为我在MySpringSecurityService类中删除了add角色权限
到此,注解方式的角色权限配置就完成了.

小结一下: 无论是配置文件方式还是注解方式,都不是只用一个,在实际开发中,需要同时使用, 通常,我们将范围大的控制在配置文件中配置, 范围小的控制,使用注解方式配置. 说的官方一点,就是配置文件的方式,颗粒度大, 注解的方式,颗粒度小, 两者结合使用,才是最佳的.


本文结束, 感谢阅读,转载请注明原地址,请勿直接复制粘贴盗取,写博客不宜,还望尊重原创!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值