【Spring Security】六、自定义认证处理的过滤器
这里接着上一章的自定义过滤器,这里主要的是配置自定义认证处理的过滤器,并加入到FilterChain的过程。在我们自己不在xml做特殊的配置情况下,security默认的做认证处理的过滤器为UsernamePasswordAuthenticationFilter,通过查看源码知道,做认证处理的方法为attemptAuthentication,这个方法的主要作用就是将用户输入的账号和密码,封装成一个UsernamePasswordAuthenticationToken对象,然后通过setDetails方法将这个对象储存起来,然后调用this.getAuthenticationManager().authenticate(authRequest)方法返回一个Authentication对象。其中这个过程this.getAuthenticationManager().authenticate(authRequest)又调用的其他的许多类,这里简单的讲解下:
UsernamePasswordAuthenticationFilter-->ProviderManager-->AbstractUserDetailsAuthenticationProvider-->DaoAuthenticationProvider-->JdbcDaoImpl
- 当输入用户名和密码后,点击登陆到达UsernamePasswordAuthenticationFilter的attemptAuthentication方法,这个方法是登陆的入口
- 然后其调用ProviderManager中的authenticate方法,而ProviderManager委托给AbstractUserDetailsAuthenticationProvider的authenticate做
- 然后AbstractUserDetailsAuthenticationProvider调用DaoAuthenticationProvider中的retrieveUser
- 在DaoAuthenticationProvider类的retrieveUser方法中,因为要通过输入的用户名获取到一个UserDetails,所以其调用JdbcDaoImpl中的loadUserByUsername方法,该方法给它的调用者返回一个查询到的用户(UserDetails),最终AbstractUserDetailsAuthenticationProvider的authenticate方法中会得到一个UserDetails对象user
- 然后接着执行preAuthenticationChecks.check(user)和additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);其中前面这个方法是判断,查询的用户是否可用或被锁等,后面的则是判断查询到的user对象的密码是否和authentication(这个对象其实就是存储用户输入的用户名和密码)的密码一样,若一样则表示登陆成功,若错误,则throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);Bad credentials这个消息就是登陆失败后的信息。
初步的讲解了登陆过程中类的调用,那么下面这个例子就是自定义一个MyUsernamePasswordAuthenticationFilter来代替默认的 UsernamePasswordAuthenticationFilter。
一、自定义MyUsernamePasswordAuthenticationFilter
package com.sunny.auth; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter{ public static final String USERNAME = "j_username"; public static final String PASSWORD = "j_password"; /** * 用户登录验证方法入口 */ @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (!request.getMethod().equals("POST")) { throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); } String username = this.obtainUsername(request); String password = this.obtainPassword(request); // 加密密码(根据“密码{用户名})进行加密 // String sh1Password = password + "{" + username + "}"; // PasswordEncoder passwordEncoder = new StandardPasswordEncoderForSha1(); // String result = passwordEncoder.encode(sh1Password); // UserInfo userDetails = (UserInfo) // userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); // Allow subclasses to set the "details" property setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } /** * 获取用户名 */ @Override protected String obtainUsername(HttpServletRequest request) { Object obj = request.getParameter(USERNAME); return null == obj ? "" : obj.toString().trim().toLowerCase(); } /** * 获取密码 */ @Override protected String obtainPassword(HttpServletRequest request) { Object obj = request.getParameter(PASSWORD); return null == obj ? "" : obj.toString(); } }
上述的代码这样写其实和默认的UsernamePasswordAuthenticationFilter并没有什么区别,但是这里主要是学会将自定义的Filter加入到security中的FilterChain中去,实际上这个方法中,一般会直接验证用户输入的和通过用户名从数据库里面查到的用户的密码是否一致,如果不一致,就抛异常,否则继续向下执行。
二、配置MyUsernamePasswordAuthenticationFilter并将其加入到FilterChain中去
- MyUsernamePasswordAuthenticationFilter有filterProcessesUrl属性为登陆的过滤的地址
- authenticationManager为authentication-manager标签中配置的东西
- authenticationSuccessHandler为验证成功后跳转的处理器
- authenticationFailureHandler为验证失败的处理器
- 另外还要配置一个出登陆引导的处bean:LoginUrlAuthenticationEntryPoint
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <!-- 不需要访问权限 --> <http pattern="/page/login.jsp" security="none"></http> <http auto-config="false" entry-point-ref="loginUrlAuthenticationEntryPoint"> <!-- <form-login login-page="/page/login.jsp" default-target-url="/page/admin.jsp" authentication-failure-url="/page/login.jsp?error=true" /> --> <logout invalidate-session="true" logout-success-url="/page/login.jsp" logout-url="/j_spring_security_logout" /> <!-- 自定义登录过滤器 --> <custom-filter ref="myUsernamePasswordAuthenticationFilter" position="FORM_LOGIN_FILTER" /> <!-- 通过配置custom-filter来增加过滤器,before="FILTER_SECURITY_INTERCEPTOR"表示在SpringSecurity默认的过滤器之前执行。 --> <custom-filter ref="filterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR" /> </http> <!-- 登录切入点 --> <beans:bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"> <beans:property name="loginFormUrl" value="/page/login.jsp"/> </beans:bean> <!-- 自定义登录过滤器 --> <beans:bean id="myUsernamePasswordAuthenticationFilter" class="com.sunny.auth.MyUsernamePasswordAuthenticationFilter"> <beans:property name="filterProcessesUrl" value="/j_spring_security_check" /> <beans:property name="authenticationManager" ref="authenticationManager" /> <beans:property name="authenticationSuccessHandler" ref="loginLogAuthenticationSuccessHandler" /> <beans:property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler" /> </beans:bean> <!-- 登录成功 --> <beans:bean id="loginLogAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler"> <beans:property name="targetUrlParameter" value="/page/admin.jsp" /> </beans:bean> <!-- 登录失败 --> <beans:bean id="simpleUrlAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"> <beans:property name="defaultFailureUrl" value="/page/login.jsp" /> </beans:bean> <!-- 认证过滤器 --> <beans:bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor"> <!-- 用户拥有的权限 --> <beans:property name="accessDecisionManager" ref="accessDecisionManager" /> <!-- 用户是否拥有所请求资源的权限 --> <beans:property name="authenticationManager" ref="authenticationManager" /> <!-- 资源与权限对应关系 --> <beans:property name="securityMetadataSource" ref="securityMetadataSource" /> </beans:bean> <!-- 授权管理器 --> <beans:bean id="accessDecisionManager" class="com.sunny.auth.MyAccessDecisionManager"> </beans:bean> <!--自定义的切入点--> <beans:bean id="securityMetadataSource" class="com.sunny.auth.MyFilterInvocationSecurityMetadataSource"> <beans:property name="builder" ref="builder"/> </beans:bean> <beans:bean id="builder" class="com.sunny.auth.JdbcRequestMapBulider"> <beans:property name="dataSource" ref="dataSource" /> <beans:property name="resourceQuery" value="select re.res_string,r.name from role r,resc re,resc_role rr where r.id=rr.role_id and re.id=rr.resc_id" /> </beans:bean> <!--认证管理--> <authentication-manager alias="authenticationManager"> <authentication-provider> <jdbc-user-service data-source-ref="dataSource" users-by-username-query="select username,password,status as enabled from user where username = ?" authorities-by-username-query="select user.username,role.name from user,role,user_role where user.id=user_role.user_id and user_role.role_id=role.id and user.username=?" /> </authentication-provider> </authentication-manager> </beans:beans>
三、验证

这段代码中targetUrlParameter本来已经拿到值了,但是经过判断后没有直接给targetUrl,却从request里把页面跳转的路径当作了request的参数来获取value,头大大的
修改方式一:
将成功跳转的属性名称targetUrlParameter改为defaultTargetUrl
修改方式二:
自己实现AuthenticationSuccessHandler接口,或者继承SimpleUrlAuthenticationSuccessHandler类并重写方法
我采用第一种,增加一个SavedRequestAwareAuthenticationSuccessHandler类
package com.sunny.auth; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; /** * 自定义登录成功后的处理 * @ClassName: SparkAuthenticationSuccessHandler * @Description: TODO(这里用一句话描述这个类的作用) * @author Sunny * @date 2018年7月6日 上午9:20:56 * */ public class CustomLoginSuccessHandler implements AuthenticationSuccessHandler{ Log logger = LogFactory.getLog(CustomLoginSuccessHandler.class); private String targetUrlParameter; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { logger.info("登录成功,即将forward:" + this.targetUrlParameter); // 登录成功之后将SECURITY放入上下文中 request.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext()); request.getRequestDispatcher(this.targetUrlParameter).forward(request, response); } public String getTargetUrlParameter() { return targetUrlParameter; } public void setTargetUrlParameter(String targetUrlParameter) { this.targetUrlParameter = targetUrlParameter; } }
然后把成功跳转的bean改为自定义的类
<beans:bean id="loginLogAuthenticationSuccessHandler" class="com.sunny.auth.CustomLoginSuccessHandler"> <beans:property name="targetUrlParameter" value="/page/admin.jsp" /> </beans:bean>
然后重新启动下,验证OK
【Spring Security】六、自定义认证处理的过滤器的更多相关文章
- spring security 3 自定义认证,授权示例
1,建一个web project,并导入所有需要的lib. 2,配置web.xml,使用Spring的机制装载: <?xml version="1.0" encoding=& ...
- Spring Security OAuth2.0认证授权六:前后端分离下的登录授权
历史文章 Spring Security OAuth2.0认证授权一:框架搭建和认证测试 Spring Security OAuth2.0认证授权二:搭建资源服务 Spring Security OA ...
- [权限管理系统(四)]-spring boot +spring security短信认证+redis整合
[权限管理系统]spring boot +spring security短信认证+redis整合 现在主流的登录方式主要有 3 种:账号密码登录.短信验证码登录和第三方授权登录,前面一节Sprin ...
- 认证与授权】Spring Security系列之认证流程解析
上面我们一起开始了Spring Security的初体验,并通过简单的配置甚至零配置就可以完成一个简单的认证流程.可能我们都有很大的疑惑,这中间到底发生了什么,为什么简单的配置就可以完成一个认证流程啊 ...
- Spring Security OAuth2.0认证授权四:分布式系统认证授权
Spring Security OAuth2.0认证授权系列文章 Spring Security OAuth2.0认证授权一:框架搭建和认证测试 Spring Security OAuth2.0认证授 ...
- spring security 表单认证的流程
spring security表单认证过程 表单认证过程 Spring security的表单认证过程是由org.springframework.security.web.authentication ...
- Spring Security 解析(二) —— 认证过程
Spring Security 解析(二) -- 认证过程 在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因此决定先把Spring Security .S ...
- Spring Security OAuth2.0认证授权三:使用JWT令牌
Spring Security OAuth2.0系列文章: Spring Security OAuth2.0认证授权一:框架搭建和认证测试 Spring Security OAuth2.0认证授权二: ...
- Springboot WebFlux集成Spring Security实现JWT认证
我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶! 1 简介 在之前的文章<Springboot集成Spring Security实现JWT认证>讲解了如何在传统 ...
随机推荐
- JTable的模型
2018-11-04 23:15:21开始写 模型类 import javax.swing.table.DefaultTableModel;//导入包 public class LocalTableM ...
- eclipse更换workspace需要重新设置的内容
.jdk Window-->java-->Installed JREs,新增或修改你所需要的jdk版本,点击需要的jdk-->edit 在Default VM arguments里面 ...
- 【Hbase学习之三】Hbase Java API
环境 虚拟机:VMware 10 Linux版本:CentOS-6.5-x86_64 客户端:Xshell4 FTP:Xftp4 jdk8 hadoop-2.6.5 hbase-0.98.12.1-h ...
- 多选插件multiselect.js
官方网址:http://loudev.com/ html: <html> <head> <link href="path/to/multiselect.css& ...
- Centos7 安装 apache + php7.0 环境
安装apache rpm -qa|grep httpd 查看是否安装 yum install httpd 安装 service httpd start 启动服务 测试是否 启动 I ...
- Always an integer UVALive - 4119
题目很简单,就是求表达式(P/D)的结果是不是整数.其中P是一个整系数的多项式,D是一个正整数. 把1-k(最高次)+1都试一次就好了.结论可以总结归纳得到.(k取 0, 1, 2 .... 的情况推 ...
- Cheerleaders UVA - 11806
题目大意是: 在一个m行n列的矩形网格中放置k个相同的石子,问有多少种方法?每个格子最多放一个石子,所有石子都要用完,并且第一行.最后一行.第一列.最后一列都要有石子. 容斥原理.如果只是n * m放 ...
- centos 内存清理
在清理前内存使用情况 free -m 用以下命令清理内存 echo 1 > /proc/sys/vm/drop_caches 清理后内存使用情况再用以下命令看看. free –m 多出很多内存了 ...
- flask自动代码自动补全
编写py文件时,无法补全: 在app对象后面添加:# type:Flask app=Flask(__name__) # type:Flask from flask import Flask, fl ...
- 探索RequestBody报com.alibaba.fastjson.JSONObject cannot be cast to xxx
今天使用RequestBody接受前端传过来的参数,以前接受字符串数组非常成功,这次把形参改成了List<User>,原本以为顺利接受参数并映射成User的list结构,结果竟然在我取us ...