前言
SpringSecurity 是 spring 采用 AOP 思想,基于 servlet 过滤器实现的安全框架。它提供了完善的认证机制和方法级的授权功能。是一款非常优秀的权限管理框架。
权限管理,一般指根据系统设置的安全规则或者安全策略,用户可以访问而且只能访问自己被授权的资源。权限管理几乎出现在任何系统里面,前提是需要有用户和密码认证的系统。
认证:通过用户名和密码成功登陆系统后,让系统得到当前用户的角色身份。
授权:系统根据当前用户的角色,给其授予对应可以操作的权限资源。
核心内容
SpringSecurity所需依赖
SpringSecurity需要的引入的依赖,
spring-security-core.jar 核心包,任何 SpringSecurity 功能都需要此包。
spring-security-web.jar web 工程必备,包含过滤器和相关的 Web 安全基础结构代码。
spring-security-config.jar 用于解析 xml 配置文件,用到 SpringSecurity 的 xml配置文件的就要用到此包。
spring-security-taglibs.jarSpringSecurity 提供的动态标签库,jsp 页面可以用。
<dependencies>
<!--spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.25</version>
</dependency>
<!--springmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.25</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.25</version>
</dependency>
<!--servlet-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
<scope>test</scope>
</dependency>
<!--SpringSecurity-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.3.13.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>5.3.13.RELEASE</version>
</dependency>
</dependencies>
由于和 web 工程集成,我们在项目中可以不用引入 core 和 web 的依赖。因为 spring 工程有 spring-core 包,springmvc 有 spring-web 包。可以依赖传递过来。
配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<!--配置监听器,在项目启动时加载SpringSeurity核心配置文件-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springSecurity.xml</param-value>
</context-param>
<!--
配置认证授权过滤器
这里filter-name的名称,只能是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>
<!--编码过滤器-->
<!--前端控制器-->
</web-app>
SpringSuecrity的核心配置文件
<?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: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/security
http://www.springframework.org/schema/security/spring-security.xsd">
<security:http security="none" pattern="/css/**"></security:http>
<security:http security="none" pattern="/img/**"></security:http>
<security:http security="none" pattern="/plugins/**"></security:http>
<security:http auto-config="true" use-expressions="true">
<security:intercept-url pattern="/login.jsp" access="permitAll()"></security:intercept-url>
<security:intercept-url pattern="/**" access="hasAnyRole('ROLE_ADMIN','ROLE_USER')"></security:intercept-url>
<security:form-login login-page="/login.jsp"
login-processing-url="/login"
username-parameter="username"
password-parameter="password"
default-target-url="/index.jsp"
authentication-failure-forward-url="/failer.jsp"/>
<security:csrf disabled="true"/>
<security:logout logout-url="/logout"
invalidate-session="true"
logout-success-url="/login.jsp"/>
<security:remember-me token-validity-seconds="3600"/>
</security:http>
<!--配置认证管理器-->
<security:authentication-manager>
<!--配置认证提供者-->
<security:authentication-provider user-service-ref="userServiceImpl">
<!--配置加密方式-->
<security:password-encoder ref="bCryptPasswordEncoder"></security:password-encoder>
</security:authentication-provider>
</security:authentication-manager>
<!--配置加密工具类-->
<bean id="bCryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>
</beans>
细节:
<security:http security="none" pattern="/css/**"></security:http>
静态资源不过滤,直接放行,没有什么好说的<security:http auto-config="true" use-expressions="true"> 3/4/5/6/7/8 </security:http>
auto-config=“true”: 开启Spring-Security 的自动配置
use-expressions=“true”: 允许使用Spring表达式<security:intercept-url pattern="/login.jsp" access="permitAll()"></security:intercept-url>
表示登录页面可以被任何用户访问,不需要任何权限<security:intercept-url pattern="/**" access="hasAnyRole('ROLE_ADMIN','ROLE_USER')"></security:intercept-url>
pattern: 拦截的请求资源
access: 访问资源所需要的角色
这里access有很多写法,例如:access="hasAnyAuthority('ADMIN','USER')"
- 自定义登录页面
这里为什么说是自定义一个登录页面呢?难道还有一个自带的登陆页面?没错,SpringSecurity本身携带一个巨丑的首页。
<security:form-login login-page="/login.jsp"
login-processing-url="/login"
username-parameter="username"
password-parameter="password"
default-target-url="/index.jsp"
authentication-failure-forward-url="/failer.jsp"/>
login-page:自定义的登录页面
login-processing-url:表单提交之后,交给 action 去处理 springSecurity 默认提供了一个叫/login 的逻辑处理。
username-parameter:值必须和登录表单里面的控件名称保持一致。
password-parameter:值必须和登录表单里面的控件名称保持一致。
authentication-failure-forward-url:认证失败之后,需要跳转的页面。
default-target-url:认证成功之后,需要跳转的页面。
-
<security:csrf disabled="true"/>
ajax 提交时使用这个<security:csrfMetaTags/>
CSRF(Cross-siterequestforgery)跨站请求伪造,是一种难以防范的网络攻击方式。
关闭csrf,默认是开启的,开启后项目中的所有表单提交都必须携带<security:csrfInput/>
,不推荐禁用,关闭后不太安全,这里关闭是因为,我授权认证是我项目写完之后加上的。 -
配置注销登录后跳转的页面
<security:logout logout-url="/logout"
<!--销毁session-->
invalidate-session="true"
<!--注销成功后返回登录页-->
logout-success-url="/login.jsp"/>
<security:remember-me token-validity-seconds="3600"/>
开启一定时间的免密登录
token-validity-seconds:指定时间,单位秒
注意:第3-8点都是在2标签内部
- 实现认证功能和密码加密
<!--配置认证管理器-->
<security:authentication-manager>
<!--配置认证提供者 注入-->
<security:authentication-provider user-service-ref="userServiceImpl">
<!--配置加密方式-->
<security:password-encoder ref="bCryptPasswordEncoder"></security:password-encoder>
</security:authentication-provider>
</security:authentication-manager>
<!--配置密码加密工具类-->
<bean id="bCryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>
实现步骤:
- 让我们自己的 UserService 接口继承 UserDetailsService
public interface UserService extends UserDetailsService{}
- 定义 UserServiceImpl 类,实现这个接口,编写 loadUserByUsername 业务
public class UserServiceImpl implements UserService {
//重写方法 核心方法
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserInfo userInfo = this.findUserByUserName(username);
List<Role> roles = userInfo.getRoles();
User user = new User(userInfo.getUsername(),userInfo.getPassword(),getAuthorities(roles));
return user;
}
/**
* 获取所有的权限集合
*/
private Collection<? extends GrantedAuthority> getAuthorities(List<Role> roles) {
List<SimpleGrantedAuthority> authorityList = new ArrayList<>();
for (Role role : roles) {
authorityList.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleName()));
// List<Permission> permissions = role.getPermissions();
// for (Permission permission : permissions) {
// authorityList.add(new SimpleGrantedAuthority("ROLE_" + permission.getPermissionName()));
// }
}
return authorityList;
}
}
授权
说明:SpringSecurity 可以通过注解的方式来控制类或者方法的访问权限。注解需要对应的注解支持,若注解放在 controller 类中,对应注解支持应该放在 mvc 配置文件中,因为 controller 类是有 mvc 配置文件扫描并创建的,同理,注解放在 service 类中,对应注解支持应该放在 spring 配置文件中。
<!--开启使用注解表达式使用支持-->
<security:global-method-security pre-post-annotations="enabled" proxy-target-class="true"/>
- @Secured
@Secured 是专门用于判断是否具有角色的。能写在方法或类上。 参数要以 ROLE_开头. - @PreAuthorize/@PostAuthorize
@PreAuthorize 和@PostAuthorize 都是方法或类级别注解。
@PreAuthorize 表示访问方法或类在执行之前先判断权限,大多情况下都是使用这个注解,注解的参数和access()方法参数取值相同,都是权限表达式。
@PostAuthorize 表示方法或类执行结束后判断权限,此注解很少被使用到