Spring Security构建Rest服务-0702-个性化用户认证流程2
登录成功后的处理AuthenticationSuccessHandler:
认证成功后,默认情况下spring security会继续访问之前访问的url,如果想自定义处理逻辑,用默认的就不行了。此时需要自定义登录成功后的处理,springsecurity提供了一个接口,AuthenticationSuccessHandler:
有三个参数,request,response,authentication(封装认证的信息,认证请求里的信息ip、session等)
public interface AuthenticationSuccessHandler {
/**
* Called when a user has been successfully authenticated.
*
* @param request the request which caused the successful authentication
* @param response the response
* @param authentication the <tt>Authentication</tt> object which was created during
* the authentication process.
*/
void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException;
}
这个接口只有一个方法,登录成功后就被调用。
1、自定义一个handler,实现这个接口:
package com.imooc.security.browser.authentication; import java.io.IOException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.ObjectMapper; /**
* 认证成功后做的处理
* ClassName: ImoocAuthenticationSuccessHandler
* @Description: 认证成功后做的处理
* @author lihaoyang
* @date 2018年3月1日
*/
@Component("imoocAuthenticationSuccessHandler")
public class ImoocAuthenticationSuccessHandler implements AuthenticationSuccessHandler{ private Logger logger = LoggerFactory.getLogger(getClass()); //springmvc启动会自动注册一个ObjectMapper
@Autowired
private ObjectMapper objectMapper; @Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException { logger.info("登录成功");
//把authentication返回给响应
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(authentication));
} }
2、添加到配置类,让spring security执行自定义的处理器
package com.imooc.security.browser; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import com.imooc.security.core.properties.SecurityProperties; @Configuration //这是一个配置
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter{ //读取用户配置的登录页配置
@Autowired
private SecurityProperties securityProperties; //自定义的认证后的处理器
@Autowired
private AuthenticationSuccessHandler imoocAuthenticationSuccessHandler; //注意是org.springframework.security.crypto.password.PasswordEncoder
@Bean
public PasswordEncoder passwordencoder(){
//BCryptPasswordEncoder implements PasswordEncoder
return new BCryptPasswordEncoder();
} //版本一:配置死的登录页
// @Override
// protected void configure(HttpSecurity http) throws Exception {
// //实现需要认证的接口跳转表单登录,安全=认证+授权
// //http.httpBasic() //这个就是默认的弹框认证
// http.formLogin() //表单认证
// .loginPage("/login.html") //登录页面
// //登录过滤器UsernamePasswordAuthenticationFilter默认登录的url是"/login",在这能改
// .loginProcessingUrl("/authentication/form")
// .and()
// .authorizeRequests() //下边的都是授权的配置
// .antMatchers("/login.html").permitAll() //放过登录页不过滤,否则报错
// .anyRequest() //任何请求
// .authenticated() //都需要身份认证
// .and()
// .csrf().disable() //关闭csrf防护
// ;
// } //版本二:可配置的登录页
@Override
protected void configure(HttpSecurity http) throws Exception {
//实现需要认证的接口跳转表单登录,安全=认证+授权
//http.httpBasic() //这个就是默认的弹框认证
http.formLogin() //表单认证
.loginPage("/authentication/require") //处理用户认证BrowserSecurityController
//登录过滤器UsernamePasswordAuthenticationFilter默认登录的url是"/login",在这能改
.loginProcessingUrl("/authentication/form")
.successHandler(imoocAuthenticationSuccessHandler)//自定义的认证后处理器
.and()
.authorizeRequests() //下边的都是授权的配置
// /authentication/require:处理登录,securityProperties.getBrowser().getLoginPage():用户配置的登录页
.antMatchers("/authentication/require",securityProperties.getBrowser().getLoginPage()).permitAll() //放过登录页不过滤,否则报错
.anyRequest() //任何请求
.authenticated() //都需要身份认证
.and()
.csrf().disable() //关闭csrf防护
;
}
}
启动springboot,访问localhost:8080/index.html ,跳转到登录页:

登录,响应了自定义的处理器,返回了Authentication的json:其中粉色部分是自定义的登录处理MyUserDetailService类的loadUserByUsername方法返回值User类对应的json ,而且spring security还会自动把密码置为null

{
"authorities": [{
"authority": "admin"
}],
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": "DBDC15FA0FFEFD35B011431454B4F466"
},
"authenticated": true,
"principal": {
"password": null,
"username": "user",
"authorities": [{
"authority": "admin"
}],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true
},
"credentials": null,
"name": "user"
}
MyUserDetailService:
package com.imooc.security.browser; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component; /**
* UserDetailsService是SpringSecurity的一个接口,
* 只有一个方法:根据用户名获取用户详情
* ClassName: MyUserDetailService
* @Description: TODO
* @author lihaoyang
* @date 2018年2月28日
*/
@Component
public class MyUserDetailService implements UserDetailsService{ private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired
private PasswordEncoder passwordEncoder; /**
* UserDetails接口,实际可以自己实现这个接口,返回自己的实现类
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
logger.info("登录用户名:"+username);
//根据用户名查询用户信息 //User:springsecurity 对 UserDetails的一个实现
//为了演示在这里用passwordEncoder加密一下密码,实际中在注册时就加密,此处直接拿出密码
String password = passwordEncoder.encode("123456");
System.err.println("加密后密码: "+password);
//参数:用户名|密码|是否启用|账户是否过期|密码是否过期|账户是否锁定|权限集合
return new User(username,password,true,true,true,true,AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
} }
登录失败的处理AuthenticationFailureHandler:
和登录成功的处理一样
package com.imooc.security.browser.authentication; import java.io.IOException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.ObjectMapper; /**
* 登录失败后的处理
* ClassName: ImoocAuthenticationFailureHandler
* @Description: 登录失败后的处理
* @author lihaoyang
* @date 2018年3月1日
*/
@Component("imoocAuthenticationFailureHandler")
public class ImoocAuthenticationFailureHandler implements AuthenticationFailureHandler { private Logger logger = LoggerFactory.getLogger(getClass()); //springmvc启动会自动注册一个ObjectMapper
@Autowired
private ObjectMapper objectMapper; @Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException { logger.info("登录失败"); //把authentication返回给响应
//状态码500,服务器内部错误
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(exception));
} }
在java配置类里注入,然后配置一下就行了


启动springboot,访问登录页http://localhost:8080/demo-login.html,填写错误的密码:

响应500,坏的凭证。和一些堆栈信息


到目前为止,自定义登录处理逻辑已经做完了,此时做的效果都是返回的json,如果系统里是登录成功后跳转页面的,这样还不够通用,需要做成返回json和重定向两种都适应的。
要的效果是,能够在application.properties里做登录类型的配置。如:

需要在配置类 SecurityProperties 里加上这个loginType,新建枚举类:

package com.imooc.security.core.properties; /**
* 登录类型枚举
* ClassName: LoginType
* @Description: 登录类型
* @author lihaoyang
* @date 2018年3月1日
*/
public enum LoginType { /**
* 返回json
*/
JSON, /**
* 重定向
*/
REDIRECT
}
BrowserProperties :
package com.imooc.security.core.properties; /**
* 浏览器配置项
* ClassName: BrowserProperties
* @Description: 浏览器配置项
* @author lihaoyang
* @date 2018年2月28日
*/
public class BrowserProperties { //用户未配置默认登录页
private String loginPage = "/login.html"; private LoginType loginType = LoginType.JSON ; public String getLoginPage() {
return loginPage;
} public void setLoginPage(String loginPage) {
this.loginPage = loginPage;
} public LoginType getLoginType() {
return loginType;
} public void setLoginType(LoginType loginType) {
this.loginType = loginType;
} }
动态配置登录类型
改造登录处理器:
登录成功处理器改造为:
继承一个spring默认的实现类 extends SavedRequestAwareAuthenticationSuccessHandler ,引入配置类,就能读取properties配置文件,进行判断了:
package com.imooc.security.browser.authentication; import java.io.IOException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.ObjectMapper;
import com.imooc.security.core.properties.BrowserProperties;
import com.imooc.security.core.properties.LoginType;
import com.imooc.security.core.properties.SecurityProperties; /**
* 认证成功后做的处理
* ClassName: ImoocAuthenticationSuccessHandler
* @Description: 认证成功后做的处理
* @author lihaoyang
* @date 2018年3月1日
*/
@Component("imoocAuthenticationSuccessHandler")
public class ImoocAuthenticationSuccessHandler
//spring默认的登录成功处理器,实现了AuthenticationSuccessHandler接口,适配登录后 重定向和返回json两种用这个实现
extends SavedRequestAwareAuthenticationSuccessHandler
//返回json用这个实现
/*implements AuthenticationSuccessHandler*/{ private Logger logger = LoggerFactory.getLogger(getClass()); //springmvc启动会自动注册一个ObjectMapper
@Autowired
private ObjectMapper objectMapper; @Autowired
private SecurityProperties securityProperties; @Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException { logger.info("登录成功"); /**
* 判断配置的登录类型,做不同处理
*/
if(LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())){
//响应json
//把authentication返回给响应
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(authentication));
}else{
//调用父类的方法,默认就是重定向
super.onAuthenticationSuccess(request, response, authentication);
} } }
登录失败的处理改造为:
package com.imooc.security.browser.authentication; import java.io.IOException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.ObjectMapper;
import com.imooc.security.core.properties.BrowserProperties;
import com.imooc.security.core.properties.LoginType;
import com.imooc.security.core.properties.SecurityProperties; /**
* 登录失败后的处理
* ClassName: ImoocAuthenticationFailureHandler
* @Description: 登录失败后的处理
* @author lihaoyang
* @date 2018年3月1日
*/
@Component("imoocAuthenticationFailureHandler")
public class ImoocAuthenticationFailureHandler
extends SimpleUrlAuthenticationFailureHandler
/*implements AuthenticationFailureHandler*/ { private Logger logger = LoggerFactory.getLogger(getClass()); //springmvc启动会自动注册一个ObjectMapper
@Autowired
private ObjectMapper objectMapper; @Autowired
private SecurityProperties securityProperties; @Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException { logger.info("登录失败");
if(LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())){
//把authentication返回给响应
//状态码500,服务器内部错误
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(exception));
}else{
super.onAuthenticationFailure(request, response, exception);
}
}
}
这样,在application.properties 里配置 imooc.security.browser.loginType = REDIRECT 或 imooc.security.browser.loginType = JSON 就可以动态控制登录成功后改返回json还是重定向了。
在demo项目新建一个index.html:

<body>
<h2>Index Page</h2> <br>
</body>
配置登录类型为REDIRECT,访问 localhost:8080/index.html 让登录

登录成功后,重定向到了index.html

设置为JSON登录:
#登录类型
imooc.security.browser.loginType = JSON
再访问localhost:8080/index.html :

响应我们自己处理的登录逻辑。
完整代码放在了github :https://github.com/lhy1234/spring-security
Spring Security构建Rest服务-0702-个性化用户认证流程2的更多相关文章
- Spring Security构建Rest服务-0701-个性化用户认证流程
上一篇说了用户认证的基本流程,但是上一篇当访问一个受保护的服务后,如果未认证会调到默认的登录页面,这样是不行的,而且认证成功后,就直接访问了那个服务,如果想要做认证成功后做一些操作,还需要自定义. 个 ...
- Spring Security构建Rest服务-1001-spring social开发第三方登录之spring social基本原理
OAuth协议是一个授权协议,目的是让用户在不将服务提供商的用户名密码交给第三方应用的条件下,让第三方应用可以有权限访问用户存在服务提供商上的资源. 接着上一篇说的,在第三方应用获取到用户资源后,如果 ...
- Spring Security构建Rest服务-1300-Spring Security OAuth开发APP认证框架之JWT实现单点登录
基于JWT实现SSO 在淘宝( https://www.taobao.com )上点击登录,已经跳到了 https://login.taobao.com,这是又一个服务器.只要在淘宝登录了,就能直接访 ...
- Spring Security构建Rest服务-1202-Spring Security OAuth开发APP认证框架之重构3种登录方式
SpringSecurityOAuth核心源码解析 蓝色表示接口,绿色表示类 1,TokenEndpoint 整个入口点,相当于一个controller,不同的授权模式获取token的地址都是 /oa ...
- Spring Security构建Rest服务-1201-Spring Security OAuth开发APP认证框架之实现服务提供商
实现服务提供商,就是要实现认证服务器.资源服务器. 现在做的都是app的东西,所以在app项目写代码 认证服务器: 新建 ImoocAuthenticationServerConfig 类,@Ena ...
- Spring Security构建Rest服务-1203-Spring Security OAuth开发APP认证框架之短信验证码登录
浏览器模式下验证码存储策略 浏览器模式下,生成的短信验证码或者图形验证码是存在session里的,用户接收到验证码后携带过来做校验. APP模式下验证码存储策略 在app场景下里是没有cookie信息 ...
- Spring Security构建Rest服务-1200-SpringSecurity OAuth开发APP认证框架
基于服务器Session的认证方式: 前边说的用户名密码登录.短信登录.第三方登录,都是普通的登录,是基于服务器Session保存用户信息的登录方式.登录信息都是存在服务器的session(服务器的一 ...
- Spring Security构建Rest服务-0900-rememberMe记住我
Spring security记住我基本原理: 登录的时候,请求发送给过滤器UsernamePasswordAuthenticationFilter,当该过滤器认证成功后,会调用RememberMeS ...
- Spring Security构建Rest服务-1205-Spring Security OAuth开发APP认证框架之Token处理
token处理之二使用JWT替换默认的token JWT(Json Web Token) 特点: 1,自包含:jwt token包含有意义的信息 spring security oauth默认生成的t ...
- Spring Security构建Rest服务-1204-Spring Security OAuth开发APP认证框架之Token处理
token处理之一基本参数配置 处理token时间.存储策略,客户端配置等 以前的都是spring security oauth默认的token生成策略,token默认在org.springframe ...
随机推荐
- SQL之经典SQL语句大全
经典SQL语句大全 一.基础 1.说明:创建数据库CREATE DATABASE database-name 2.说明:删除数据库drop database dbname3.说明:备份sql serv ...
- Python+Android开发
1 下载Scripting Layer for Android (SL4A) Scripting Layer for Android (SL4A) 是一个开源项目,目标是为android系统提供脚本语 ...
- 团队博客-第六周:Alpha阶段项目复审(科利尔拉弗队)
团队的排名-点评:以下排名点评谨代表个人观点,如有冒犯,评论联系删除 小组名字和链接 优点 缺点,bug报告(至少140字) 最终名次(无并列) 中午吃啥队 微信小程序应用,新型app会是一个便利的使 ...
- oracle 11g Enterprise Manager配置失败
Enterprise Manager以下简称em,Database Configuration Assistant简称DBCA. 病症 监听程序未启动或数据库服务未注册到该监听程序.启动该监听程序并注 ...
- LeetCode146:LRU Cache
题目: Design and implement a data structure for Least Recently Used (LRU) cache. It should support the ...
- JS获取用户的Ip地址
在网站中通常需要获取使用者的ip地址,获取抵制的方式有很多,这里就简单介绍一下js获取用户ip地址 /*使用的新浪的ip查询api,根据返回的数据进行判断*/ <script src=" ...
- 窗口与导航-----Selenium快速入门(十三)
前面所讲的,大部分是WebDriver这个接口以及相关的类的使用.而本文所讲的窗口与导航,也是里面的内容,而且非常简单,目测就能学会. 一.窗口,也就是window,这里的窗口是指浏览器窗口.他的方法 ...
- 在循环中使用鼠标悬停时表示当前悬停选中,传入this关键字即可
在前端循环中使用鼠标悬停事件 <div class="message-widget contact-widget"> <!-- Message --> {% ...
- C#一些代码小结--文件对话框
C# 一些代码小结--文件对话框 查看文件完整路径 try { Config cfg = new Config(); var file = ""; if (saveFileDial ...
- iOS 多Target, Other link Flag
在创建多个马甲包或者多个App间只有很小的差异是使用多Target是一种很好的方法 https://www.jianshu.com/p/18db54655246 1:选中原始的Target, 点击右键 ...