每篇一句
江月何年初照人,江畔何人初见月 —— 唐 · 张若虚《春江花月夜》
认证和授权概念
- 认证:系统提供的用于识别用户身份的功能,通常提供用户名和密码进行登录其实就是在进行认证,认证的目的是让系统知道你是谁。
- 授权:用户认证成功后,需要为用户授权,其实就是指定当前用户可以操作哪些功能。
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>
快速入门
- 创建Maven工程 springsecurity_demo,导入坐标
这一步就略过, maven坐标简介中已经介绍了, 因为整合spring,所以记得导入spring相关坐标
- 创建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的拦截规则和认证管理器
- 配置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
- 测试
配置完以上内容之后,在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进阶中还存在如下一些问题:
- 直接将用户名和密码配置在了配置文件中,而真实生产环境下的用户名和密码往往保存在数据库中。
- 在配置文件中配置的密码使用明文,这非常不安全,而真实生产环境下密码需要进行加密。
从数据库中查询用户信息 :
如果我们要从数据库动态查询用户信息,就必须按照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角色权限
到此,注解方式的角色权限配置就完成了.
小结一下: 无论是配置文件方式还是注解方式,都不是只用一个,在实际开发中,需要同时使用, 通常,我们将范围大的控制在配置文件中配置, 范围小的控制,使用注解方式配置. 说的官方一点,就是配置文件的方式,颗粒度大, 注解的方式,颗粒度小, 两者结合使用,才是最佳的.
本文结束, 感谢阅读,转载请注明原地址,请勿直接复制粘贴盗取,写博客不宜,还望尊重原创!