Spring security 学习记录
1、Spring security 简介
? Spring Security 为 Java EE-based 企业软件应用程序提供全面的安全服务(也就是用户登录页面和相关权限的控制),应用的安全性包括用户认证( Authentication )和用户权限( Authorization )两部分。 用户认证是确定某个用户是否有进入系统的权限,使用用户名密码去认证,也就是所谓的登录;用户权限是确定哪些用户有哪些功能权限,一般都是按角色。
2、主要过滤器
? 众所周知 想要对对Web资源进行保护,最好的办法莫过于Filter,要想对方法调用进行保护,最好的办法莫过于AOP。所以springSecurity在我们进行用户认证以及授予权限的时候,通过各种各样的拦截器来控制权限的访问,从而实现安全。
有篇关于过滤器的文章,有需要可以查看下
https://blog.csdn.net/andy_zhang2007/article/details/84726992
- WebAsyncManagerIntegrationFilter
为请求处理过程中可能发生的异步调用准备安全上下文获取途径
- SecurityContextPersistenceFilter
整个请求处理过程所需的安全上下文对象SecurityContext的准备和清理不管请求是否针对需要登录才能访问的页面,这里都会确保SecurityContextHolder中出现一个SecurityContext对象:
1.未登录状态访问登录保护页面:空SecurityContext对象,所含Authentication为null
2.登录状态访问某个页面:从SecurityContextRepository获取的SecurityContext对象 - HeaderWriterFilter
将指定的头部信息写入响应对象
- CorsFilter
对请求进行csrf保护
- LogoutFilter
检测用户退出登录请求并做相应退出登录处理
- RequestCacheAwareFilter
提取请求缓存中缓存的请求
1.请求缓存在安全机制启动时指定
2.请求写入缓存在其他地方完成
3.典型应用场景:
用户请求保护的页面,
系统引导用户完成登录认证,
然后自动跳转到到用户最初请求页面 - SecurityContextHolderAwareRequestFilter
包装请求对象使之可以访问SecurityContextHolder,从而使请求真正意义上拥有接口HttpServletRequest中定义的getUserPrincipal这种访问安全信息的能力
- AnonymousAuthenticationFilter
如果当前SecurityContext属性Authentication为null,将其替换为一个AnonymousAuthenticationToken`
- SessionManagementFilter
检测从请求处理开始到目前是否有用户登录认证,如果有做相应的session管理,比如针对为新登录用户创建新的session(session fixation防护)和设置新的csrf token等。
- ExceptionTranslationFilter
处理AccessDeniedException和 AuthenticationException异常,将它们转换成相应的HTTP响应
- FilterSecurityInterceptor
一个请求处理的安全处理过滤器链的最后一个,检查用户是否已经认证,如果未认证执行必要的认证,对目标资源的权限检查,如果认证或者权限不足,抛出相应的异常:AccessDeniedException或者AuthenticationException
- UsernamePasswordAuthenticationFilter
检测用户名/密码表单登录认证请求并作相应认证处理:
1.session管理,比如为新登录用户创建新session(session fixation防护)和设置新的csrf token等
2.经过完全认证的Authentication对象设置到SecurityContextHolder中的SecurityContext上;
3.发布登录认证成功事件InteractiveAuthenticationSuccessEvent
4.登录认证成功时的Remember Me处理
5.登录认证成功时的页面跳转 - BasicAuthenticationFilter 检测和处理http basic认证
- DefaultLoginPageGeneratingFilter 生成缺省的登录页面
- DefaultLogoutPageGeneratingFilter 生成缺省的退出登录页面
- RememberMeAuthenticationFilter 针对Remember Me登录认证机制的处理逻辑 (免登陆)
3、security核心组件
- SecurityContextHolder:提供对SecurityContext的访问
- SecurityContext,:持有Authentication对象和其他可能需要的信息
- UsernamePasswordAuthenticationFilter 检测用户民密码并做处理
- AuthenticationManager 其中可以包含多个AuthenticationProvider
- ProviderManager对象为AuthenticationManager接口的实现类
- AuthenticationProvider 主要用来进行认证操作的类 调用其中的authenticate()方法去进行认证操作
- Authentication:Spring Security方式的认证主体
- GrantedAuthority:对认证主题的应用层面的授权,含当前用户的权限信息,通常使用角色表示
- UserDetails:构建Authentication对象必须的信息,可以自定义,可能需要访问DB得到
- UserDetailsService:通过username构建UserDetails对象,通过loadUserByUsername根据userName获取UserDetail对象 (可以在这里基于自身业务进行自定义的实现 如通过数据库,xml,缓存获取等)
- passwordEncoder 密码加密器
4、主要流程
- 如图:当用户登录时,前端将用户输入的用户名、密码信息传输到后台( UsernamePasswordAuthenticationFilter ),后台用一个类对象将其封装起来,通常使用的是UsernamePasswordAuthenticationToken这个类,然后在 AuthenticationManager 中获取用户,进行加密,认证对比等相关操作,返回是否是系统用户。
- 比较两者的密码,如果密码正确就成功登陆,同时把包含着用户的用户名、密码、所具有的权限等信息的类对象放到SecurityContextHolder(安全上下文容器,类似Session)中去。
- 用户访问一个资源的时候,首先判断是否是受限资源。如果是的话还要判断当前是否未登录,没有的话就跳到登录页面。
- 如果用户已经登录,访问一个受限资源的时候,程序要根据url去数据库中取出该资源所对应的所有可以访问的角色,然后拿着当前用户的所有角色一一对比,判断用户是否可以访问。
- 用户认证的实现方式有很多种,主要体现在 AuthenticationProvider 接口的实现中,目前主流的实现:
DaoAuthenticationProvider 利用数据库数据进行登录认证
JassAuthenticationProvider Java 认证和授权服务
CasAuthenticationProvider 利用单点登录进行登录认证
LdapAuthenticationProvider 跨域身份认证
5、简单代码实现
本例子是基于老版本3.2.7的XML一种实现,需要基于confirm配置,请查看更高版本,地址如下:
https://www.docs4dev.com/docs/zh/spring-security/4.2.10.RELEASE/reference
web.xml配置,添加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>
Security.xml配置
具体配置讲解可以参考
<sec:http pattern="/loggedOut.html" security="none" />
<!-- 配置免登录的资源 -->
<sec:http pattern="/toLogin" security="none" />
<sec:http pattern="/loginError" security="none" />
<!-- IS_AUTHENTICATED_ANONYMOUSLY 允许匿名用户进入 -->
<!-- IS_AUTHENTICATED_FULLY 允许登录用户进入 -->
<!-- IS_AUTHENTICATED_REMEMBERED 允许登录用户和rememberMe用户进入 -->
<!--auto-config = true 则使用from-login. 如果不使用该属性 则默认为http-basic(没有session).-->
<!-- lowercase-comparisons:表示URL比较前先转为小写。-->
<!-- path-type:表示使用Apache Ant的匹配模式。-->
<!--access-denied-page:访问拒绝时转向的页面。-->
<!-- access-decision-manager-ref:指定了自定义的访问策略管理器。-->
<!--isAuthenticated() 当前用户是否已通过身份验证-->
<!--access="hasRole('SUPER_ADMIN') 代表该地址只有权限SUPER_ADMIN才能查看-->
<sec:http use-expressions="true" auto-config="true" >
<sec:intercept-url pattern="/user/*" access="hasRole('SUPER_ADMIN')" />
<sec:intercept-url pattern="/**" access="isAuthenticated()" />
<!--login-page:指定登录页面。 -->
<!-- login-processing-url:指定了客户在登录页面中按下 Sign In 按钮时要访问的 URL。-->
<!-- authentication-failure-url:指定了身份验证失败时跳转到的页面。-->
<!-- default-target-url:指定了成功进行身份验证和授权后默认呈现给用户的页面。-->
<!-- always-use-default-target:指定了是否在身份验证通过后总是跳转到default-target-url属性指定的URL。
-->
<sec:form-login login-page="/toLogin"
login-processing-url="/login"
always-use-default-target="true"
default-target-url="/index"
authentication-failure-url="/toLogin?error=1"
/>
<!--logout-url:指定了用于响应退出系统请求的URL。其默认值为:/j_spring_security_logout。-->
<!-- logout-success-url:退出系统后转向的URL。-->
<!-- invalidate-session:指定在退出系统时是否要销毁Session。-->
<sec:logout invalidate-session="true" logout-success-url="/toLogin"
logout-url="/j_spring_cas_security_logout" />
<!-- 实现免登陆验证 -->
<sec:remember-me />
</sec:http>
<bean id="myFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="casAuthenticationManager" />
</bean>
<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider"
p:userDetailsService-ref="userDetailsService" p:passwordEncoder-ref="md5PasswordEncoder">
</bean>
<!--md5加密方式-->
<bean id = "md5PasswordEncoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"></bean>
<!--数据使用直接查询数据库方式,也可以实现userDetailsService接口-->
<bean id="casDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${JDBC.driverClassName}" />
<property name="url" value="${JDBC.url}" />
<property name="username" value="${JDBC.username}" />
<property name="password" value="${JDBC.password}" />
</bean>
<sec:jdbc-user-service id="userDetailsService" data-source-ref="casDataSource"
users-by-username-query="
SELECT
u.account username,
u.`password`,
u.displayFlag enabled
FROM
core_user AS u
WHERE
u.displayFlag = 1
AND
u.enabledFlag = 1
AND
u.account = ? "
authorities-by-username-query="
SELECT
u.account username,
r.`code` role
FROM
core_role AS r
LEFT JOIN core_user_role AS ur ON r.id = ur.roleId
INNER JOIN core_user AS u ON ur.userId = u.id
WHERE
u.displayFlag = 1
AND
u.enabledFlag = 1
AND
u.account = ?" />
login.jsp如下
<form id="fm1" action="/login" method="post" novalidate="novalidate">
<div class="form-group form-username">
<input type="text" class="form-control" id="username" name="j_username" placeholder="Name">
</div>
<div class="form-group form-password">
<input id="password" name="j_password" type="password">
</div>
<div class="form-checkbox">
<span class="checkbox pointer checked">
<input type="checkbox" id="_spring_security_remember_me" name="_spring_security_remember_me" checked="checked">
</span> 7天免登录
</div>
<div class="form-group">
<button type="submit" class="btn id="loginBtn">登 录
</button>
</div>
</form>
注意:如果是使用UsernamePasswordAuthenticationFilter ,得注意使用的版本,每个版本对应的字段可能不一样,需要打开源码看看。
在3.2.7中,用户名(j_username),密码(j_password),记住密码(_spring_security_remember_me)。
在4.2中,用户名(username),密码(password)
在学习的过程中,借鉴了前辈们的文章,参考如下
https://blog.csdn.net/liushangzaibeijing/article/details/81220610
https://blog.csdn.net/andy_zhang2007/article/details/84726992
https://www.docs4dev.com/docs/zh/spring-security/4.2.10.RELEASE/reference
原文地址:https://www.cnblogs.com/yz-yang/p/11697942.html