1. 权限管理
根据系统设置的安全策略或者安全规则,用户可以访问而且只能访问自己被授权的资源。比如,超级管理员和普通管理员有不同的权限,比如黄金会员和白银会员也有不同的权限,这些都是权限管理
先要实行权限管理,前提是需要有用户和密码认证的系统
认证:通过用户名和密码成功登录系统后,让系统得到当前用户的身份
授权:系统根据当前用户的角色,给其授予对应可以操作的权限资源
2. 完成权限管理需要三个对象
用户:主要包含用户名,密码和当前用户的角色信息,可实现认证操作
角色:主要包含角色名称,角色描述和当前角色拥有的权限信息,可实现授权操作
权限:权限也可以称为菜单,主要包含当前权限名称,url地址信息可实现动态展示菜单
3. Spring Security概念
Spring Security是基于servlet过滤器实现的安全框架,提供了完善的认证机制和方法级的授权功能
3.1 Spring Security的主要jar包及其主要作用
spring-security-core.jar核心包,任何的Spring Security功能都需要此包spring-security-web.jarweb工程需要,包含过滤器和相关的web安全基础结构代码spring-security-config.jar用于解析xml配置文件,用到Spring Security的xml的配置文件就需要用到此包spring-security-tablibs.jarSpring Security提供的动态标签库,jsp页面可以使用
3.2 在SSM中使用Spring Security来实现hello world
首先是进行配置,这里SSM的相关配置不再赘述,然后我们来说Spring Security的配置文件
首先在web.xml文件中配置Spring security的核心--org.springframework.web.filter.DelegatingFilterProxy
<!--SpringSecurity核心过滤器链-->
<!--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>
然后需要一个Spring-security的配置文件,命名为spring-security.xml
<?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 https://www.springframework.org/schema/security/spring-security.xsd">
<!-- 设置可以用spring的el表达式来配置spring security并自动生成对应配置组件(过滤器)
auto-config="true" 表示自动加载配置文件
-->
<security:http auto-config="true" use-expressions="true">
<!-- 资源拦截 -->
<!-- 使用spring的el表达式来指定项目所有资源访问都必须具有ROLE_USER或者ROLE_ADMIN角色
pattern="/**" 表示拦截所有的请求,这里用双**,表示的是下一层路径和所有的子路径
-->
<security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER')"/>
</security:http>
<!--设置Spring Security认证用户信息的来源-->
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<!-- {noop}表示不加密认证 -->
<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>
然后需要让Spring的ioc容器引入spring-security.xml文件,也就是在applicationContext.xml文件中加入:
<!--引入springsecurity的配置文件-->
<!-- 在父容器中引入配置问价 -->
<import resource="classpath:spring-security.xml"/>
附带文件目录:
然后直接启动项目,我们会发现页面如下:
注意,这个页面我的代码中完全没有写过,而且我的入口路径是/index,而这里的路径是/login
解惑:这个页面是Spring Security提供的一个认证页面!
当我们输入用户名和密码,根据上面配置文件的这个部分:
<security:user name="user" password="{noop}user" authorities="ROLE_USER"/>
我们输入用户名密码都是user,然后见证奇迹!
进入到了我们熟悉的Hello World页面!
4. 使用自定义的登录界面
4.1 在spring-security.xml配置文件中增加配置
<!-- 释放静态资源,设置security="none"表示不需要经过spring security处理 -->
<security:http pattern="/css/**" security="none"/>
<security:http pattern="/img/**" security="none"/>
<security:http pattern="/plugins/**" security="none"/>
<!-- 设置可以用spring的el表达式来配置spring security并自动生成对应配置组件(过滤器)
auto-config="true" 表示自动加载配置文件
-->
<security:http auto-config="true" use-expressions="true">
<!-- 让认证页面能够匿名访问 -->
<security:intercept-url pattern="/login.jsp" access="permitAll()"/>
<!-- 资源拦截 -->
<!-- 使用spring的el表达式来指定项目所有资源访问都必须具有ROLE_USER或者ROLE_ADMIN角色
pattern="/**" 表示拦截所有的请求,这里用双**,表示的是下一层路径和所有的子路径
-->
<security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER')"/>
<!-- 如果想要使用自定义的登录页面,进行以下配置 -->
<!-- 分别代表: 登录页面的链接 登录数据提交的链接(form表单里面的action) 登陆成功之后的跳转链接 -->
<security:form-login login-page="/login.jsp"
login-processing-url="/login"
default-target-url="/index.jsp"/>
<security:logout logout-url="/logout"
logout-success-url="/login.jsp"/>
</security:http>
启动项目之后输入用户名密码登录,会出现403错误,原因是权限不足,这是由于Spring Security中的csrf保护机制
csrf(跨站请求伪造攻击)简单来说就是获取到了你登录某个系统的cookie,然后使用你的cookie信息去进行非法登录从而带来危害
解决方式1:禁用csrf(不推荐)
添加配置项:
<security:http auto-config="true" use-expressions="true">
<!-- 让认证页面能够匿名访问 -->
<security:intercept-url pattern="/login.jsp" access="permitAll()"/>
<!-- 资源拦截 -->
<!-- 使用spring的el表达式来指定项目所有资源访问都必须具有ROLE_USER或者ROLE_ADMIN角色
pattern="/**" 表示拦截所有的请求,这里用双**,表示的是下一层路径和所有的子路径
-->
<security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER')"/>
<!-- 如果想要使用自定义的登录页面,进行以下配置 -->
<!-- 分别代表: 登录页面 登录的连接 登陆成功之后的跳转链接 -->
<security:form-login login-page="/login.jsp"
login-processing-url="/login"
default-target-url="/index.jsp"/>
<security:logout logout-url="/logout"
logout-success-url="/login.jsp"/>
<!-- 去掉csrf拦截的过滤器 -->
<security:csrf disabled="true"/>
</security:http>
这种方式不推荐,直接禁用csrf,不能保证安全
解决方式2:在请求中添加token
查看Spring security提供的login页面能够看到页面上的html元素有一个表单,内容为:
所以我们在请求中也需要添加这样的 _csrf 值,这里使用的jsp页面中能够直接使用Spring Security提供的标签
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="security"%>
<form action="${pageContext.request.contextPath}/login" method="post">
<security:csrfInput/>
...
添加上这个csrfInput标签之后就能够携带csrf的token
注:HttpSessionCsrfTokenRepository对象负责生成token并放入session域中。
5. 使用自定义的认证流程
通过查看源码能够发现需要实现一个UserDetailsService接口并且将实现类添加到配置中
public class UserService implements UserDetailsService {
/**
* @param username 用户在浏览器输入的用户名
* @return UserDetail Spring Security自己的用户对象
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 第一步 根据用户名做查询
// 从数据中进行查询,查询到具体一个用户的信息
// 然后将信息构造成一个UserDetail的对象返回
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
// 添加角色
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
return new User(username, "password", authorities);
// return null是认证失败的意思
// return null;
}
}
<bean id="userService" class="cn.fan.demo.service.UserService"/>
<security:authentication-manager>
<security:authentication-provider user-service-ref="userService">
<security:password-encoder ref="passwordEncoding"/>
</security:authentication-provider>
</security:authentication-manager>
6. 加密认证
Spring Security提供了对密码进行加密的类BCryptPasswordEncoder,能够对密码进行动态加盐加密!
添加这个加密对象:
<!--加密对象-->
<bean id="passwordEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
<!--设置Spring Security认证用户信息的来源-->
<security:authentication-manager>
<security:authentication-provider user-service-ref="userServiceImpl">
<!--指定认证使用的加密对象-->
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
如果要使用解密对象对密码进行加密,那么密码前面就不能够使用{noop}来表示明文了。同时在保存用户信息的时候,也需要保存加密之后的密码