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、主要流程

  1. 如图:当用户登录时,前端将用户输入的用户名、密码信息传输到后台( UsernamePasswordAuthenticationFilter ),后台用一个类对象将其封装起来,通常使用的是UsernamePasswordAuthenticationToken这个类,然后在 AuthenticationManager 中获取用户,进行加密,认证对比等相关操作,返回是否是系统用户。

  2. 比较两者的密码,如果密码正确就成功登陆,同时把包含着用户的用户名、密码、所具有的权限等信息的类对象放到SecurityContextHolder(安全上下文容器,类似Session)中去。

  3. 用户访问一个资源的时候,首先判断是否是受限资源。如果是的话还要判断当前是否未登录,没有的话就跳到登录页面。

  4. 如果用户已经登录,访问一个受限资源的时候,程序要根据url去数据库中取出该资源所对应的所有可以访问的角色,然后拿着当前用户的所有角色一一对比,判断用户是否可以访问。

  5. 用户认证的实现方式有很多种,主要体现在 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配置

具体配置讲解可以参考

https://www.cnblogs.com/yyxxn/p/8080141.html

<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>&nbsp;&nbsp;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

posted on 2019-10-18 15:16  吃羊的草  阅读(3726)  评论(0编辑  收藏  举报