Spring Security构建Rest服务-0701-个性化用户认证流程
上一篇说了用户认证的基本流程,但是上一篇当访问一个受保护的服务后,如果未认证会调到默认的登录页面,这样是不行的,而且认证成功后,就直接访问了那个服务,如果想要做认证成功后做一些操作,还需要自定义。
个性化用户认证流程:
1)自定义登录页面
2)自定义登录成功处理(如给用户发积分或者签到)
3)自定义登录失败处理(如记录密码失败次数,超过3次不让登录等)
1,自定义登录页面
在BrowserSecurityConfig类里配置:
@Configuration //这是一个配置
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter{ //注意是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(); //都需要身份认证
}
}
在src/main/resource下新建resources目录,新建login页面

<body>
<h2>登录页</h2>
<form action="/authentication/form" method="post">
<table border="1">
<tr>
<td>用户名:</td>
<td><input type="text" name="username"/></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"/></td>
</tr>
<tr>
<td colspan="2" align="right"><button type="submit">登录</button></td>
</tr>
</table>
</form>
</body>
访问 http://localhost:8080/user跳转到自定义的登录页:

此时点击登录:

SpringSecurity默认提供了CSRF(跨站请求伪造)防护,是用CSRF token来完成防护的。暂时关闭CSRF防护,在BrowserSecurityConfig里设置:

然后再访问localhost:8080/user,登录成功后,返回查询信息

到此为止,自定义登录页已经做好了,但是我们发出的是Rest服务,应该返回状态码+json信息,而这里返回时html页,是不合理的,可以做成像SpringBoot对错误处理的机制一样,如果是浏览器发过来的请求,就跳转到登录页,如果是app发过来的请求,就返回Json。
而且随后要做成可重用的安全模块,现在限制死了登录页,是不合理的。
解决:
1,处理不同类型的请求

最终的项目结构:
浏览器项目:

核心项目:

引用安全模块的demo项目:

思路:在浏览器项目里新建一个控制器BrowserSecurityController,处理用户认证,浏览器和app的请求做不同的处理。在core核心项目里,封装读取application.properties里自定义配置的类,这样在demo项目引用开发的安全模块时,可以根据application.properties里的配置,跳转到自定义的登录页。
第一步,BrowserSecurityConfig配置的configure方法中,http.formLogin() .loginPage("/authentication/require") 标红处就不应该是一个login.html的登录页了,让他跳转到自定义的BrowserSecurityController,浏览器和app的请求做不同的处理,SpringSecurity里有一些工具类RequestCache可以帮我们拿到请求的url:
/**
* 处理用户认证Controller,浏览器和app的请求做不同的处理
* ClassName: BrowserSecurityController
* @Description: 处理用户认证Controller,浏览器和app的请求做不同的处理
* @author lihaoyang
* @date 2018年2月28日
*/
@RestController
public class BrowserSecurityController { private Logger logger = LoggerFactory.getLogger(getClass()); //缓存的请求,SpringSecurity通过HttpSessionRequestCache把请求信息缓存到session里
private RequestCache requestCache = new HttpSessionRequestCache();
//跳转的工具
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); @Autowired
private SecurityProperties securityProperties; /**
* 当需要身份认证时,跳转到这里处理
* @Description: TODO
* @param @param request
* @param @param response
* @param @return
* @return String
* @throws Exception
* @throws
* @author lihaoyang
* @date 2018年2月28日
*/
@RequestMapping("/authentication/require")
@ResponseStatus(code=HttpStatus.UNAUTHORIZED)//返回状态码401 未授权
public SimpleResponse requireAuthentication(HttpServletRequest request,HttpServletResponse response) throws Exception{
//拿出缓存的请求 引发跳转的请求
SavedRequest savedRequest = requestCache.getRequest(request, response);
if(savedRequest != null){
//拿到引发请求的url
String targetUrl = savedRequest.getRedirectUrl();
logger.info("引发跳转的url:"+targetUrl);
if(StringUtils.endsWithIgnoreCase(targetUrl, ".html")){//请求是否以.html结尾
redirectStrategy.sendRedirect(request, response, securityProperties.getBrowser().getLoginPage());//要跳转的页面,此处应该做成可配置的页面
}
}
return new SimpleResponse("访问的服务需要身份认证,请引导用户到登录页");
} }
现在需要处理的就是上边标红的代码,他的作用是能够动态配置登录页,下一步,在demo项目的application.properties里配置登录页:
#自定义登录页
imooc.security.browser.loginPage = /demo-login.html
现在问题就是读取这个配置,在core核心项目里,做配置的封装:

SecurityProperties:
package com.imooc.security.core.properties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; /**
* 自定义配置项
* ClassName: SecurityProperties
* @Description: 自定义配置项
* 这个类会读取application.properties里所有以imooc.security开头的配置项
*
* imooc.security.browser.loginPage = /demo-login.html
* 其中的browser的配置会读取到BrowserProperties中去
* 这是以点分割的,一级一级的和类的属性对应
* @author lihaoyang
* @date 2018年2月28日
*/ @ConfigurationProperties(prefix="imooc.security")
public class SecurityProperties { private BrowserProperties browser = new BrowserProperties(); public BrowserProperties getBrowser() {
return browser;
} public void setBrowser(BrowserProperties browser) {
this.browser = browser;
} }
BrowserProperties:
package com.imooc.security.core.properties; /**
* 浏览器配置项
* ClassName: BrowserProperties
* @Description: 浏览器配置项
* @author lihaoyang
* @date 2018年2月28日
*/
public class BrowserProperties { private String loginPage = "login.html"; //用户未配置默认登录页 public String getLoginPage() {
return loginPage;
} public void setLoginPage(String loginPage) {
this.loginPage = loginPage;
} }
SecurityCoreConfig:
package com.imooc.security.core; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration; import com.imooc.security.core.properties.SecurityProperties; /**
* 使配置SecurityProperties生效
* ClassName: SecurityCoreConfig
* @Description: 使配置SecurityProperties生效
* @author lihaoyang
* @date 2018年2月28日
*/
@Configuration
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityCoreConfig { }
这三个类就和application.properties里的配置:imooc.security.browser.loginPage = /demo-login.html 对应上了。
在需要读取配置的地方,直接注入SecurityCoreConfig即可,此时在 BrowserSecurityConfig 里注入SecurityCoreConfig 配置,即可让过滤器读取到配置的登录页。
@Configuration //这是一个配置
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter{ //读取用户配置的登录页配置
@Autowired
private SecurityProperties securityProperties; //注意是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")
.and()
.authorizeRequests() //下边的都是授权的配置
// /authentication/require:处理登录,securityProperties.getBrowser().getLoginPage():用户配置的登录页
.antMatchers("/authentication/require",securityProperties.getBrowser().getLoginPage()).permitAll() //放过登录页不过滤,否则报错
.anyRequest() //任何请求
.authenticated() //都需要身份认证
.and()
.csrf().disable() //关闭csrf防护
;
}
}
此时再访问localhost:8080/user ,会响应未登录信息和401状态码。此时发ajax请求的前端就可以引导用户到登录页去。


此时访问:http://localhost:8080/index.html,会跳转到demo项目配置的登录页。

注释掉demo项目的登录页配置再访问,#imooc.security.browser.loginPage = /demo-login.html ,就会跳转到browser项目配置的 /login.html 登录页

上述过程只是完成了开篇说的个性化用户认证流程中的第一个步骤,自定义登录页面,下一篇说其他两项。
github代码:https://github.com/lhy1234/spring-security
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 ...
随机推荐
- UVa 11384 Help is needed for Dexter (递归)
题意:给定一个n表示1到n的序列,让你用最小的步数把这个序列都变为0,每个操作可以从序列中选择一个或多个个,同时减掉一个正整数,求最少的步数. 析:一看这个题,感觉挺高深的,但是静下心来想想,其实挺简 ...
- qt编程遇到的东西
setWindowFlags http://blog.chinaunix.net/uid-23500957-id-3876399.html move()方法,的作用是设置QWidget部件的pos坐标 ...
- MOD13A1: MODIS/Terra Vegetation Indices 16-Day L3 Global 500 m SIN Grid V006
https://lpdaac.usgs.gov/node/838 Description The MOD13A1 Version 6 product provides a Vegetation Ind ...
- 19、Docker Compose
编排(Orchestration)功能是复杂系统实现灵活可操作性的关键.特别是docker应用场景中,编排意味着用户可以灵活地对各种容器资源实现定义和管理. 在我们部署多容器的应用时: 要从D ...
- [转载]HTML5游戏前端开发秘籍
http://isux.tencent.com/html5-game-development-cheats.html 转载至腾讯ISUX HTML5游戏前端开发秘籍 本文由米随随编写 QQ空间Andr ...
- VS2010+Oracle11+Entity Framework4.1环境搭建及常见问题
在微软的实体数据模型中存在四种查询方式: SQL字符串:Linq:Linq to SQL:Linq to Entity(ESQL) 对于Linq SQL目前微软虽然仍在支持,但微软已经声明不再推荐. ...
- 跨终端Web
1.终端vs设备 H5页面运行在同一设备的不同终端下. (1)Web浏览器. (2)微信.QQ浏览器. (3)移动App的Webview. (4)TV机顶盒. 2.跨终端的实现方式 (1)响应式 存在 ...
- .NET 任务调度Quartz系列(1)——自建定时任务
在我们平时项目中经常会遇到定时任务,比如定时同步数据,定时备份数据,定时统计数据等,定时任务我们都知道使用Quartz.net,此系列写的也是Quartz,但是在此之前,我们先用其他方式做个简单的定时 ...
- 关于ORACLE的字符窜存储(未完善,欢迎补充)
oracle中常见的用于存储字符串的数据类型有: 数据类型 是否定长 最多存储数 效率排行 备注 是否oracle特有 英文占位 中文占位 char 是 2000 比VARCHAR2稍高 char的长 ...
- linux 基本操作笔记
linux文件系统的实现 linux有一个树状结构来组织文件,数的顶端为根目录/,节点为目录,而末节点为所包含的数据文件.我们可以对文件进行多种操作,比如打开和读写. 存储设备分区 文 ...