• Spring security基本使用


    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

  • 相关阅读:
    如何面试程序员?
    类似猪八戒网的网站
    存储过程
    一个不错的网站(博客制作参考)
    用触发器来实现级联更新级联删除
    用触发器进行级联删除
    数据库触发器详解
    浅谈数据库中的存储过程
    JDBC连接数据库
    Java递归函数
  • 原文地址:https://www.cnblogs.com/yz-yang/p/11697942.html
Copyright © 2020-2023  润新知