一、入门案例
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.hcx</groupId>
<artifactId>springsecuritydemo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>springsecuritydemo Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>5.0.2.RELEASE</spring.version>
<spring.security.version>5.0.1.RELEASE</spring.security.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>springsecuritydemo</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>springsecuritydemo</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-security.xml</param-value>
</context-param>
<listener>
<!--<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>-->
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--springSecurityFilterChain不能变,只能是这个名字-->
<filter>
<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>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
spring-security.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<security:http auto-config="true" use-expressions="false">
<!--
配置资源路径,表示任意路径都需要ROLE_USER权限
intercept-url:过滤规则,
pattern:对哪些url进行权限控制,
access:表示在请求对应的url时需要什么权限。
默认配置时它应该是一个以逗号分隔的角色列表,请求的用户只需拥有其中的一个角色就能成功访问对应的URL
-->
<security:intercept-url pattern="/**" access="ROLE_USER"/>
</security:http>
<!--创建两个用户,并指定用户名和密码-->
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="user" password="{noop}user"
authorities="ROLE_USER"/>
<security:user name="admin" password="{noop}admin"
authorities="ROLE_ADMIN"/>
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
</beans>
访问 http://localhost:8090/login.html(端口根据自己配置的来,login.html是自带的登录页)

根据配置,配置了两类用户 user和admin:
使用user登陆,默认访问index.jsp页面

使用admin登陆:

配置了需要
ROLE_USER
的身份才可以登陆,admin用户配置的身份为ROLE_ADMIN
,所以没有权限。
二、使用自定义页面
在webapp下定义三个页面:

login.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<form action="login" method="post">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"/></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="登录"/>
<input type="reset" value="重置"/></td>
</tr>
</table>
</form>
</body>
</html>
修改spring-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.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="/fail.html"/>
<security:http auto-config="true" use-expressions="false">
<!--
配置资源路径,表示任意路径都需要ROLE_USER权限
intercept-url:过滤规则,
pattern:对哪些url进行权限控制,
access:表示在请求对应的url时需要什么权限。
默认配置时它应该是一个以逗号分隔的角色列表,请求的用户只需拥有其中的一个角色就能成功访问对应的URL
-->
<security:intercept-url pattern="/**" access="ROLE_USER"/>
<!-- 自定义登陆页面,login-page 自定义登陆页面 authentication-failure-url 用户权限校验失败之后才会跳转到这个页面,如果数据库中没有这个用户则不会跳转到这个页面。
default-target-url 登陆成功后跳转的页面。 注:登陆页面用户名固定 username,密码 password,action:login -->
<security:form-login login-page="/login.html"
login-processing-url="/login" username-parameter="username"
password-parameter="password" authentication-failure-url="/fail.html"
default-target-url="/success.html" authentication-success-forward-url="/success.html"/>
<!-- 关闭CSRF,默认是开启的 跨服务器的request forward访问-->
<security:csrf disabled="true"/>
</security:http>
<!--创建两个用户,并指定用户名和密码-->
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="user" password="{noop}user"
authorities="ROLE_USER"/>
<security:user name="admin" password="{noop}admin"
authorities="ROLE_ADMIN"/>
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
</beans>
三、使用数据库认证
三种使用方式:
①自己写实现类实现UserDetails接口(UserDetails封装用户认证信息的接口)

UserDetails用于封装当前正在进行认证的用户信息
②自己写实现类实现UserDetailsService接口:

用于规范在做认证时的具体方法
③使用Spring Security中UserDetails的实现类User

注意:使用了springSecurity之后,控制器由springSecurity完成
Demo:
UserService:
public interface UserService extends UserDetailsService{
UserInfo findById(Integer id);
void save(UserInfo userInfo);
}
UserServiceImpl:
@Service("userService")
@Transactional
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
UserInfo userInfo = null;
try {
userInfo = userMapper.selectByUsername(username);
} catch (Exception e) {
e.printStackTrace();
}
//把用户对象封装为UserDetails
//未使用密文时需要添加前缀
//User user = new User(userInfo.getUsername(),"{noop}"+userInfo.getPassword(),getAuthorities(userInfo.getRoles()));
User user = new User(userInfo.getUsername(),userInfo.getPassword(),
userInfo.getStatus()==1?true:false,true,true,true,
getAuthorities(userInfo.getRoles()));
// User user = new User(userInfo.getUsername(),"{noop}"+userInfo.getPassword(), getAuthorities(userInfo.getRoles()));
return user;
}
@Override
public void save(UserInfo userInfo) {
//对密码进行加密处理
String encodePass = bCryptPasswordEncoder.encode(userInfo.getPassword());
userInfo.setPassword(encodePass);
userMapper.save(userInfo);
}
对于使用密码加密的处理,可以直接注入BCryptPasswordEncoder类,使用bCryptPasswordEncoder.encode()方法,也可以使用工具类
密码加密工具类BCryptPasswordEncoderUtils:
package com.hcx.utils;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class BCryptPasswordEncoderUtils {
private static BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
public static String encodePassword(String password){
return bCryptPasswordEncoder.encode(password);
}
public static void main(String[] args) {
String s = encodePassword("123456");
//每次加密的结果不同
//$2a$10$04DqXfBW4UVVCuX2HZSn5e3Vm3DUfA7PUTqTMUBwVHQeEuIas5xia
//$2a$10$RQ.j.T/d4yrnu4DU9chsYObTGrsnwCgRoEaewvOzvu/ov/z/D6aCi
System.out.println(s);
}
}
springSecurity.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<!-- 配置不拦截的资源 -->
<security:http pattern="/login.jsp" security="none"/>
<security:http pattern="/fail.jsp" security="none"/>
<security:http pattern="/css/**" security="none"/>
<security:http pattern="/img/**" security="none"/>
<!--<security:http pattern="/js/**" security="none"/>-->
<security:http pattern="/plugins/**" security="none"/>
<!--
配置具体的规则
auto-config="true" 不用自己编写登录的页面,框架提供默认登录页面
use-expressions="false" 是否使用SPEL表达式
-->
<!--<security:http auto-config="true" use-expressions="false">-->
<security:http auto-config="true" use-expressions="false">
<!-- 配置具体的拦截的规则 pattern="请求路径的规则" access="访问系统的人,必须有ROLE_USER的角色" -->
<security:intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN"/>
<!--<security:intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>-->
<!-- 定义跳转的具体的页面authentication-success-forward-url="/pages/main.jsp" always-use-default-target="true" -->
<security:form-login
login-page="/login.jsp"
login-processing-url="/login.do"
default-target-url="/index.jsp"
authentication-failure-url="/fail.jsp"
authentication-success-forward-url="/pages/main.jsp"
/>
<!-- 关闭跨域请求 -->
<security:csrf disabled="true"/>
<!-- 退出 -->
<security:logout invalidate-session="true" logout-url="/logout.do" logout-success-url="/login.jsp" />
<!--<security:logout/>-->
</security:http>
<!-- 认证管理器:数据库中的用户名和密码 -->
<security:authentication-manager>
<security:authentication-provider user-service-ref="userService">
<!-- 配置加密的方式 -->
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
<!-- 配置加密类 -->
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
</beans>
web.xml
<!-- 配置加载类路径的配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext.xml,classpath*:spring-security.xml</param-value>
</context-param>
<filter>
<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>
四、服务器端方法级别权限控制
在服务器端可以通过Spring security提供的注解对方法来进行权限控制。
Spring Security在方法的权限控制上支持三种类型的注解:
- JSR-250注解
- @Secured注解
- 支持表达式的注解
注:三种注解默认都是没有启用的,需要单独通过
global-method-security
元素的对应属性进行启用
开启注解使用
①SpringSecurity.xml配置文件
<security:global-method-security jsr250-annotations="enabled"/>
<security:global-method-security secured-annotations="enabled"/>
<security:global-method-security pre-post-annotations="disabled"/>
②注解开启
@EnableGlobalMethodSecurity
:Spring Security默认是禁用注解的,要想开启注解,需要在继承WebSecurityConfigurerAdapter
的类上加@EnableGlobalMethodSecurity
注解,并在该类中将AuthenticationManager
定义为Bean。
1.JSR-250注解
使用前需要添加依赖:
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
</dependency>
①@RolesAllowed
表示访问对应方法时所应该具有的角色(标注在方法上)
@RolesAllowed({"USER", "ADMIN"})
该方法只要具有"USER", "ADMIN"任意一种权限就可以访问。
注:这里可以省略前缀ROLE_,即实际的权限是ROLE_ADMIN
②@PermitAll
表示允许所有的角色进行访问,即不进行权限控制
③@DenyAll
和PermitAll相反,表示无论什么角色都不能访问
当访问没有对应权限的方法时,页面报403:

可以设置错误页面:
web.xml:
<error-page>
<error-code>403</error-code>
<location>/403.jsp</location>
</error-page>
2.@Secured注解
该注解本身就是springsecurity包下的,所以使用时不需要像JSR-250注解一样导入依赖。
@Secured
注解标注的方法进行权限控制的支持,值默认为disabled
开启使用:
<security:global-method-security secured-annotations="enabled"/>
示例:
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account readAccount(Long id);
@Secured("ROLE_ADMIN")
注:此处不能省略前缀
3.支持表达式的注解
①@PreAuthorize
在方法调用之前,基于表达式的计算结果来限制对方法的访问
开启注解使用:
<security:global-method-security pre-post-annotations="enabled" secured-annotations="enabled"/>
示例:
@PreAuthorize("#userId == authentication.principal.userId or hasAuthority('ADMIN')")
void changePassword(@P("userId") long userId ){ }
这里表示在changePassword
方法执行之前:
- 判断方法参数userId的值是否等于principal中保存的当前用户的userId
- 当前用户是否具有ROLE_ADMIN权限
两种符合其一,就可以访问该方法。

②@PostAuthorize
允许方法调用,但是如果表达式计算结果为false,将抛出一个安全性异常
@PostAuthorize
User getUser("returnObject.userId == authentication.principal.userId or hasPermission(returnObject, 'ADMIN')");
③@PostFilter
允许方法调用,但必须按照表达式来过滤方法的结果
④@PreFilter
允许方法调用,但必须在进入方法之前过滤输入值
五、页面端标签控制权限
1.使用
在jsp页面中可以使用spring security提供的权限标签来进行权限控制
导入依赖:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>version</version>
</dependency>
页面导入标签:
<%@taglib uri="http://www.springframework.org/security/tags" prefix="security"%>
2.常用标签
在jsp中可以使用三种标签:authentication
、authorize
、accesscontrollist
其中authentication代表的是当前认证对象,可以获取当前认证对象信息,例如用户名。其它两个标签可以用于权限控制
①authentication
获取当前正在操作的用户信息
<security:authentication property="" htmlEscape="" scope="" var=""/>
- property: 只允许指定Authentication所拥有的属性,可以进行属性的级联获取,如“principle.username”,不允许直接通过方法进行调用
- htmlEscape:表示是否需要将html进行转义。默认为true。
- scope:与var属性一起使用,用于指定存放获取的结果的属性名的作用范围,默认我pageContext。Jsp中拥有的作用范围都进行进行指定
- var: 用于指定一个属性名,这样当获取到了authentication的相关信息后会将其以var指定的属性名进行存放,默认是存放在pageConext中
<%--获取当前用户的username--%>
<security:authentication property="principal.username"></security:authentication>
②authorize
用来判断普通权限,通过判断用户是否具有对应的权限而控制其所包含内容的显示
即控制页面上某些标签是否展示
<security:authorize access="" method="" url="" var=""></security:authorize>
- access: 需要使用表达式来判断权限,当表达式的返回结果为true时表示拥有对应的权限
- method:method属性是配合url属性一起使用的,表示用户应当具有指定url指定method访问的权限,method的默认值为GET,可选值为http请求的7种方法
- url:url表示如果用户拥有访问指定url的权限即表示可以显示authorize标签包含的内容
- var:用于指定将权限鉴定的结果存放在pageContext的哪个属性中
<security:authorize access="hasRole('ADMIN')">
<a href="${pageContext.request.contextPath}/user/findAll.do">
<i class="fa fa-circle-o"></i> 用户管理
</a>
</security:authorize>
注:使用了表达式,需要在springSecurity.xml配置文件中开启:
image.png
如果不开启使用表达式,可以添加配置:
<bean id="webExpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"/>
③accesscontrollist
用于鉴定ACL权限。一共定义了三个属性:hasPermission、domainObject和var,其中前两个是必须指定的。
<security:accesscontrollist hasPermission="" domainObject="" var=""></security:accesscontrollist>
- hasPermission:用于指定以逗号分隔的权限列表
- domainObject:用于指定对应的域对象
- var:用以将鉴定的结果以指定的属性名存入pageContext中,以供同一页面的其它地方使用
网友评论