该篇章会跳过Spring、SpringAOP、MyBatis基础部分的讨论,专注于Spring Security部分的讲解。
零、本文纲要
- 一、认证授权
- 认证授权
- 对象
- Spring Security
- 二、Spring Security过滤器链
补充:CsrfFilter(跨域伪造请求过滤)
- 三、快速入门
- 引入配置文件
- spring-security配置文件-配置拦截路径、登录、登出、自动配置加载
- spring-security配置文件-配置静态资源放行
- spring-security配置文件-设置认证用户的来源
- 实现UserDetailsService接口
补充:初始数据获取
一、认证授权
1. 认证授权
① 认证:通过用户名和密码成功登陆系统后,让系统得到当前用户的角色身份;
② 授权:系统根据当前用户的角色,给其授予对应可以操作的权限资源。
2. 对象
① 用户:主要包含用户名,密码和当前用户的角色信息,可实现认证操作;
② 角色:主要包含角色名称,角色描述和当前角色拥有的权限信息,可实现授权操作;
③ 权限:权限也可以称为菜单,主要包含当前权限名称,url地址等信息,可实现动态展示菜单。
一般根据对象我们也会设置相应的表格,如下:
/*sys_permission权限表*/
CREATE TABLE `sys_permission` (
`ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
`permission_NAME` varchar(30) DEFAULT NULL COMMENT '菜单名称',
`permission_url` varchar(100) DEFAULT NULL COMMENT '菜单地址',
`parent_id` int(11) NOT NULL DEFAULT '0' COMMENT '父菜单id',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*sys_role角色表*/
CREATE TABLE `sys_role` (
`ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
`ROLE_NAME` varchar(30) DEFAULT NULL COMMENT '角色名称',
`ROLE_DESC` varchar(60) DEFAULT NULL COMMENT '角色描述',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
/*sys_role_permission角色-权限关系表*/
CREATE TABLE `sys_role_permission` (
`RID` int(11) NOT NULL COMMENT '角色编号',
`PID` int(11) NOT NULL COMMENT '权限编号',
PRIMARY KEY (`RID`,`PID`),
KEY `FK_Reference_12` (`PID`),
CONSTRAINT `FK_Reference_11` FOREIGN KEY (`RID`) REFERENCES `sys_role` (`ID`),
CONSTRAINT `FK_Reference_12` FOREIGN KEY (`PID`) REFERENCES `sys_permission` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*sys_user用户表*/
CREATE TABLE `sys_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`password` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码',
`status` int(1) DEFAULT '1' COMMENT '1开启0关闭',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
/*sys_user_role用户-角色表*/
CREATE TABLE `sys_user_role` (
`UID` int(11) NOT NULL COMMENT '用户编号',
`RID` int(11) NOT NULL COMMENT '角色编号',
PRIMARY KEY (`UID`,`RID`),
KEY `FK_Reference_10` (`RID`),
CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `sys_role` (`ID`),
CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID`) REFERENCES `sys_user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3. Spring Security
Spring Security是spring采用AOP思想,基于servlet过滤器实现的安全框架。
基础依赖:
① spring-security-core:核心包,任何Spring Security功能都需要此包;
② spring-security-web:web工程必备,包含过滤器和相关的Web安全基础结构代码;
③ spring-security-config:用于解析xml配置文件,用到Spring Security的xml配置文件的就要用到此包;
④ spring-security-taglibs:Spring Security提供的动态标签库,jsp页面可以用。
注意:
spring-security-web
包中包含有spring-security-core;
spring-security-taglibs
包中包含有spring-security-web。
二、Spring Security过滤器链
Spring Security默认提供的过滤器链,如下:
- SecurityContextPersistenceFilter(容器)
- WebAsyncManagerIntegrationFilter(集成web开发)
- HeaderWriterFilter
- CsrfFilter(跨域伪造请求过滤)
- LogoutFilter(登出过滤)
- UsernamePasswordAuthenticationFilter(用户认证授权)
- DefaultLoginPageGeneratingFilter(默认登录页面)
- DefaultLogoutPageGeneratingFilter(默认退出页面)
- BasicAuthenticationFilter(以Basic开头的头信息)
- RequestCacheAwareFilter(缓存请求)
- SecurityContextHolderAwareRequestFilter(升级的容器)
- AnonymousAuthenticationFilter(匿名认证过滤器)
- SessionManagementFilter(会话管理)
- ExceptionTranslationFilter(异常处理)
- FilterSecurityInterceptor(获取所配置资源访问的授权信息,根据SecurityContextHolder中存储的用户信息来决定其是否有权
限)
补充:CsrfFilter(跨域伪造请求过滤)
- ① CsrfFilter#doFilterInternal方法
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
request.setAttribute(HttpServletResponse.class.getName(), response);
...
//此处关注CsrfFilter类的requireCsrfProtectionMatcher属性
if (!this.requireCsrfProtectionMatcher.matches(request)) {
filterChain.doFilter(request, response);
return;
}
...
filterChain.doFilter(request, response);
}
- ② DefaultRequiresCsrfMatcher类
可以看到:
"GET", "HEAD", "TRACE", "OPTIONS"四类请求可以直接通过;
除去上面四类,包括"POST","DELETE","PUT"都要被验证携带_csrf对应的token才能通过;
private static final class DefaultRequiresCsrfMatcher implements RequestMatcher {
private final HashSet<String> allowedMethods = new HashSet<>(
Arrays.asList("GET", "HEAD", "TRACE", "OPTIONS"));
@Override
public boolean matches(HttpServletRequest request) {
return !this.allowedMethods.contains(request.getMethod());
}
}
- ③ 处理方式
<%--引入security标签库--%>
<%@taglib uri="http://www.springframework.org/security/tags" prefix="security"%>
<%--在form表单内部添加如下标签,Spring Security框架会自动完成token的注入与校验--%>
<security:csrfInput/>
三、快速入门
0. 引入配置文件
applicationContext.xml配置文件中配置资源引入,如下:
<!--引入SpringSecurity配置文件-->
<import resource="classpath:spring-security.xml"/>
相当于:
@Import(SecurityConfig.class)
public class SpringConfig{...}
1. spring-security配置文件-配置拦截路径、登录、登出、自动配置加载
- ① 配置加载配置文件
<security:http auto-config="true" use-expressions="true">
auto-config="true" 表示自动加载SpringSecurity的配置文件;
use-expressions="true" 表示使用Spring的EL表达式来配置;
- ② 配置拦截资源
<security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER')"/>
pattern="/**" 表示拦截所有资源;
access="hasAnyRole('ROLE_USER') 表示只有ROLE_USER角色才能访问资源;
- ③ 配置认证信息
<security:form-login login-page="/login.jsp" login-processing-url="/login" default-target-url="/index.jsp" authentication-failure-url="/failer.jsp"/>
login-page="/login.jsp" 表示访问的登录页面
login-processing-url="/login" 表示访问的请求路径
default-target-url="/index.jsp" 表示默认认证成功的跳转页面
authentication-failure-url="/failer.jsp" 表示认证失败的跳转页面
- ④ 配置页面匿名访问
<security:intercept-url pattern="/login.jsp" access="permitAll()"/>
pattern="/login.jsp" 表示访问的资源
access="permitAll()" 表示允许该路径通过过滤器,但不会绕开Spring Security的过滤器验证
- ⑤ 配置退出登录信息
<security:logout logout-url="/logout" logout-success-url="/login.jsp"/>
logout-url="/logout" 表示登出的访问路径
logout-success-url="/login.jsp" 表示登出成功的跳转界面
<!--配置SpringSecurity-->
<!--
auto-config="true" 表示自动加载SpringSecurity的配置文件
use-expressions="true" 表示使用Spring的EL表达式来配置
-->
<security:http auto-config="true" use-expressions="true">
<!--让认证页面可以匿名访问-->
<security:intercept-url pattern="/login.jsp" access="permitAll()"/>
<!--拦截资源-->
<!--
pattern="/**" 表示拦截所有资源
access="hasAnyRole('ROLE_USER') 表示只有ROLE_USER角色才能访问资源
-->
<security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER')"/>
<!--配置认证信息-->
<security:form-login login-page="/login.jsp"
login-processing-url="/login"
default-target-url="/index.jsp"
authentication-failure-url="/failer.jsp"/>
<!--配置退出登录信息-->
<security:logout logout-url="/logout"
logout-success-url="/login.jsp"/>
<!--禁用CSRF拦截的过滤器-->
<!--<security:csrf disabled="true"/>-->
</security:http>
2. spring-security配置文件-配置静态资源放行
特殊的:/failer.jsp需要配置放行,不然登录失败页面无法正常显示。
<!--释放静态资源-->
<security:http pattern="css/**" security="none"/>
<security:http pattern="img/**" security="none"/>
<security:http pattern="plugins/**" security="none"/>
<security:http pattern="/failer.jsp" security="none"/>
3. spring-security配置文件-设置认证用户的来源
user-service-ref="userServiceImpl" 表示用户认证服务由该实现类管理;
ref="passwordEncoder" 表示使用此种加密工具类;
<!--把加密对象放入IOC容器中-->
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
<!--设置SpringSecurity认证用户的来源-->
<!--
SpringSecurity默认是时加密认证的
加上{noop}表示不加密认证
-->
<security:authentication-manager>
<security:authentication-provider user-service-ref="userServiceImpl">
<security:password-encoder ref="passwordEncoder"/>
<!--<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>
4. 实现UserDetailsService接口
user.setPassword(passwordEncoder.encode(user.getPassword()));
:保存用户信息时需要进行密码加密;
实现UserDetailsService接口,重写loadUserByUsername方法,认证通过的返回UserDetails类;
public interface UserService extends UserDetailsService{...}
@Service
@Transactional
public class UserServiceImpl implements UserService {
...
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Override
public void save(SysUser user) {
//使用BCryptPasswordEncoder进行密码加密
user.setPassword(passwordEncoder.encode(user.getPassword()));
userDao.save(user);
}
/**
* 认证业务
* @param username 用户在浏览器输入的用户名
* @return UserDetails 是Spring Security自己的用户对象
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
try {
//根据用户名做查询
SysUser sysUser = userDao.findByName(username);
if (sysUser == null) {
return null;
}
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
List<SysRole> roles = sysUser.getRoles();
for (SysRole role : roles) {
//动态赋予用户角色信息
authorities.add(new SimpleGrantedAuthority(role.getRoleName()));
}
//注意:{noop}后面的密码Spring Security会认定为原文
UserDetails userDetails = new User(
sysUser.getUsername(),
sysUser.getPassword(), //"{noop}" + sysUser.getPassword(),
authorities);
return userDetails;
} catch (Exception e) {
e.printStackTrace();
//Spring Security认为null即为认证失败
return null;
}
}
...
}
补充:初始数据获取
在引入Spring Security配置文件之前,需要先获取一下加密的密码,这样我们初始账户在开起Spring Security认证授权后才可以正常登录。获取加密密码串的简单测试方法如下:
@Test
public void encodeTest(){
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String encode = encoder.encode("123456");
//$2a$10$Vr5KdR6IR7SmGHSRGmV4puvm3RZFaWFwV5M1/WZ5pXKULatetf2.i
//$2a$10$qz98Xl2hA.FMyH13eDmf3.Pyc9.n1xsImnucyVBSCp39p3a9iH3Ye
System.out.println(encode);
}
四、结尾
以上即为Spring Security-原始篇(XML配置版)的基础内容,感谢阅读。
网友评论