1 开发基于表单的认证

Spring security核心的功能

  • 认证(你是谁?)
  • 授权(你能干什么?)
  • 攻击防护(防止伪造身份)

spring security实现了默认的用户名+密码认证,默认用户名为user,密码为:

spring security基本原理:过滤器链

  对于UsernamePasswordAuthenticationFilter只会拦截 url为/login,method为POST的请求。

1.1 自定义用户认证逻辑

1)处理用户信息获取逻辑

  UserDetailsService接口,只有一个方法:loadUserByUsername

实现该接口:数据库中存放的是加密密码,对于同一个密码不同时间的加密密文不一样

@Component
public class MyUserDetailsService implements UserDetailsService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
logger.info("用户名信息:" + s);
// 根据用户名查找用户信息
logger.info("数据库密码:" + passwordEncoder.encode("123456"));
// 用户名和密码信息用来做认证,权限信息用来对该用户做授权
return new User(s, "$2a$10$eFw06n0ABK2NFuse8y5f/eDUq7we26qQTceEtXSWNbMXnQ5Yf5Iha",
AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
}
}

2)处理用户信息校验逻辑

  处理密码加密解密:在配置文件中将PasswordEncoder对象注入spring容器,等价于@Component+包扫描组件

1.2 个性化用户认证流程

1)对于浏览器,返回自定义登录页面,让UsernamePasswordXxxFilter来处理登录请求;对于调用RESTful服务,返回json错误信息。

   用户登录成功后 ,对于浏览器,返回需要的页面;对于服务,返回json数据。

  权限配置:

    @Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()// 表单登录
.loginPage("/authentication/require") //将登录页面重定向到controller
.loginProcessingUrl("/authentication/form")
.and()
.authorizeRequests() //请求授权
.antMatchers("/authentication/require",
securityProperties.getBrowser().getLoginPage()).permitAll()//该页面允许通过
.anyRequest()
.authenticated() // 其他资源需要认证
.and()
.csrf().disable(); // 将跨站防护关掉
}

  

  控制器,根据之前URL的路径判断是否为RESTful服务,在处理

/*
当客户端发出请求,当需要认证时,spring security会重定向到该控制器
*/
@RestController
public class BrowserSecurityController {
private Logger logger = LoggerFactory.getLogger(getClass()); // 请求缓存
private RequestCache requestCache = new HttpSessionRequestCache();
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@Autowired
private SecurityProperties securityProperties;
/**
* 当需要身份认证时跳转到这里
* @param request
* @param response
* @return
*/
@RequestMapping("/authentication/require")
@ResponseStatus(code = HttpStatus.UNAUTHORIZED)
public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 判断请求类型,HTML或者app
SavedRequest savedRequest = requestCache.getRequest(request, response);
if(savedRequest!=null){
String targetUrl = savedRequest.getRedirectUrl();
logger.info("引发跳转的URL:"+targetUrl);
// 如果之前的URL为.html结尾的URL,则重定向到登录页面
if(StringUtils.endsWithIgnoreCase(targetUrl, ".html")){
redirectStrategy.sendRedirect(request, response,
securityProperties.getBrowser().getLoginPage());
}
}
return new SimpleResponse("请求的服务需要身份认证,请引导用户到登录页面");
}
}

BrowserSecurityController.java

  在启动项目中的application.properties文件中配置登录页面:

# 配置登录页面
getword.security.browser.loginPage=/demo.html

  读取配置文件信息:

  

import org.springframework.boot.context.properties.ConfigurationProperties;

// 读取前缀为getword.security的属性配置,其中browser中的属性会被读取到browserProperties中
@ConfigurationProperties(prefix = "getword.security")
public class SecurityProperties {
// browser的属性会匹配getword.security.browser后面的属性
private BrowserProperties browser = new BrowserProperties(); public BrowserProperties getBrowser() {
return browser;
} public void setBrowser(BrowserProperties browser) {
this.browser = browser;
}
}

SecurityProperties.java

public class BrowserProperties {
private String loginPage = "/login.html"; //默认值 public String getLoginPage() {
return loginPage;
} public void setLoginPage(String loginPage) {
this.loginPage = loginPage;
}
}

BrowserProperties.java

@Configuration
@EnableConfigurationProperties(SecurityProperties.class) //让属性配置读取器生效
public class SecurityCodeConfig {
}

2)自定义登录成功处理,异步登录,AuthenticationSuccessHandler接口

  自定义登录成处理:

@Component("vstudyAuthenticationSuccessHandler")
public class VstudyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
//工具类, 将对象转成json
@Autowired
private ObjectMapper objectMapper;
// 登录成功后调用
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
logger.info("登录成功");
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(objectMapper.writeValueAsString(authentication));
}
}

VstudyAuthenticationSuccessHandler

  注册,使处理器生效:

3)登录失败处理

@Component("vstudyAuthenticationFailHandler")
public class VstudyAuthenticationFailHandler implements AuthenticationFailureHandler {
@Autowired
private ObjectMapper objectMapper; private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException e) throws IOException, ServletException {
logger.info("登录失败");
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); //服务器内部错误
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(objectMapper.writeValueAsString(e));
}
}

VstudyAuthenticationFailHandler.java

配置:和success类似

4)判断请求方式,做出相应的处理

successHandler:

@Component("vstudyAuthenticationSuccessHandler")
public class VstudyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
//工具类, 将对象转成json
@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())){
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(objectMapper.writeValueAsString(authentication));
}else{
// 调用父类方法,完成重定向跳转
super.onAuthenticationSuccess(request, response, authentication);
}
}
}

VstudyAuthenticationSuccessHandler

failureHandler:

@Component("vstudyAuthenticationFailHandler")
public class VstudyAuthenticationFailHandler extends SimpleUrlAuthenticationFailureHandler {
@Autowired
private SecurityProperties securityProperties;
@Autowired
private ObjectMapper objectMapper; private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException e) throws IOException, ServletException {
logger.info("登录失败");
if(LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())){
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); //服务器内部错误
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(objectMapper.writeValueAsString(e));
}else{
super.onAuthenticationFailure(request, response, e);
}
}
}

VstudyAuthenticationFailHandler

2 认证流程

3 图形验证码

3.1 生成图形验证码

  验证码图片信息:

public class ImageCode {
private BufferedImage image;
private String code;
private LocalDateTime expireTime;//过期时间
public ImageCode(BufferedImage image, String code, int expireIn){
this.image = image;
this.code = code;
this.expireTime = LocalDateTime.now().plusSeconds(expireIn);
} public ImageCode(BufferedImage image, String code, LocalDateTime expireTime){
this.image = image;
this.code = code;
this.expireTime = expireTime;
}
}

ImageCode

  控制器:

@RestController
public class ValidateCodeController {
public static String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); @Autowired
private SecurityProperties securityProperties; @GetMapping("/image/code")
public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
ImageCode imageCode = createImageCode(new ServletWebRequest(request,response));
sessionStrategy.setAttribute(new ServletWebRequest(request, response), SESSION_KEY, imageCode);
ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream());
} /**
* 生成ImageCode验证码
* @param request
* @return
*/
public ImageCode createImageCode(ServletWebRequest request){
// 生成验证码,方法很多
int width = 60;
int height = 20;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); Random random = new Random(); g.setColor(getRandColor(200, 250));
g.fillRect(0, 0, width, height);
g.setFont(new Font("Times New Roman", Font.ITALIC, 20));
g.setColor(getRandColor(160, 200));
for (int i = 0; i < 155; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x, y, x + xl, y + yl);
} String sRand = "";
for (int i = 0; i < 4; i++) {
String rand = String.valueOf(random.nextInt(10));
sRand += rand;
g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
g.drawString(rand, 13 * i + 6, 16);
} g.dispose(); return new ImageCode(image, sRand, 100);
}
/**
* 生成随机背景条纹
*
* @param fc
* @param bc
* @return
*/
private Color getRandColor(int fc, int bc) {
Random random = new Random();
if (fc > 255) {
fc = 255;
}
if (bc > 255) {
bc = 255;
}
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
}

ValidateCodeController

3.2 验证码校验

自定义过滤器:

public class ValidateCodeFilter extends OncePerRequestFilter {
/**
* 验证码校验失败处理器
*/
private AuthenticationFailureHandler authenticationFailureHandler;
/**
* 系统配置信息
*/
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
/**
* 系统中的校验码处理器
*/ @Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 只处理登录请求
if (StringUtils.equals("/authetication/form", request.getRequestURI())
&& StringUtils.equalsIgnoreCase(request.getMethod(), "POST")) {
try {
logger.info("验证码校验通过");
} catch (ValidateCodeException e) {
//验证失败
authenticationFailureHandler.onAuthenticationFailure(request, response, e);
}
} filterChain.doFilter(request, response);
}
protected void validate(ServletWebRequest request) throws ServletRequestBindingException {
// 从session中拿到imageCode
ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request, ValidateCodeController.SESSION_KEY);
// 获取客户端输入的code,当前请求参数
String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "imageCode");
if(StringUtils.isBlank(codeInRequest)){
throw new ValidateCodeException("验证码不能为空");
}
if(codeInSession==null){
throw new ValidateCodeException("验证码不存在");
}
if(codeInSession.isExpired()){
throw new ValidateCodeException("验证码已过期");
}
if(!StringUtils.equalsIgnoreCase(codeInSession.getCode(), codeInRequest)){
throw new ValidateCodeException("验证码不匹配");
}
sessionStrategy.removeAttribute(request, ValidateCodeController.SESSION_KEY);
} public AuthenticationFailureHandler getAuthenticationFailureHandler() {
return authenticationFailureHandler;
} public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
this.authenticationFailureHandler = authenticationFailureHandler;
}
}

ValidateCodeFilter

配置:

执行流程:

/index.html ->redirect ->/authentication/require(控制器,判断是.html结尾)->login.html ->ValidateCodeFilter ->exception -> VstudyAuthenticationFailHandler ->loginType:JSON

login.html中,使用ajax发送登录请求 -> 验证码过滤器通过 -> UsernamePasswordFilter通过 -> 返回登录结果信息

3.3 验证码的接口

  • 为了方便修改验证码的参数,如宽度、高度、长度等信息,我们将通过配置文件的形式配置这些信息。还有验证码的URL地址。
  • 验证码拦截的接口可配置,比如为了限制用户操作频率,对用户操作使用验证码进行限制。验证码过滤器可以拦截多个控制器请求。
  • 验证码的生成逻辑可以配置

三级配置:

 1)验证码参数:

  默认配置:

/**
* 图形验证码
*/
public class ImageCodeProperties {
private int width = 67;
private int height = 23;
private int len = 4;
private int expireIn = 60; //60秒后过期
//seter getter
}

验证码信息:

/**
* 验证码:包括图形验证码、短信验证码
*/
public class ValidateCodeProperties {
private ImageCodeProperties image;
}

属性读取:

@ConfigurationProperties(prefix = "getword.security")
public class SecurityProperties {
// browser的属性会匹配getword.security.browser后面的属性
private BrowserProperties browser = new BrowserProperties(); // 验证码属性匹配getword.security.code后面的属性
private ValidateCodeProperties code = new ValidateCodeProperties();
}

end

spring security认证的更多相关文章

  1. Spring Security认证配置(三)

    学习本章之前,可以先了解下上篇Spring Security认证配置(二) 本篇想要达到这样几个目的: 1.登录成功处理 2.登录失败处理 3.调用方自定义登录后处理类型 具体配置代码如下: spri ...

  2. spring security 认证源码跟踪

    spring security 认证源码跟踪 ​ 在跟踪认证源码之前,我们先根据官网说明一下security的内部原理,主要是依据一系列的filter来实现,大家可以根据https://docs.sp ...

  3. Spring Security 入门(1-4-2)Spring Security - 认证过程之AuthenticationProvider的扩展补充说明

    1.用户信息从数据库获取 通常我们的用户信息都不会向第一节示例中那样简单的写在配置文件中,而是从其它存储位置获取,比如数据库.根据之前的介绍我们知道用户信息是通过 UserDetailsService ...

  4. Spring Security 入门(1-4-1)Spring Security - 认证过程

    理解时可结合一下这位老兄的文章:http://www.importnew.com/20612.html 1.Spring Security的认证过程 1.1.登录过程 - 如果用户直接访问登录页面 用 ...

  5. Authentication讲解(Spring security认证)

    标准认证过程: 1.用户使用username和password登录 2.系统验证这个password对于该username是正确的 3.假设第二步验证成功,获取该用户的上下文信息(如他的角色列表) 4 ...

  6. Spring Security认证配置(二)

    学习本章之前,可以先了解下上篇Spring Security基本配置. 本篇想要达到这样几个目的: 1.访问调用者服务时,如果是html请求,则跳转到登录页,否则返回401状态码和错误信息 2.调用方 ...

  7. Spring Security认证配置(一)

    学习本章之前,可以先了解下上篇 Spring Security基本配置. 本篇主要讲述Spring Security基于表单,自定义用户认证配置(上篇中的配置,本篇将不再阐述).一共分为三步: 1.处 ...

  8. Authentication(Spring Security 认证笔记)

    这篇文章是对Spring Security的Authentication模块进行一个初步的概念了解,知道它是如何进行用户认证的 考虑一个大家比较熟悉的标准认证过程: 1.用户使用username和pa ...

  9. spring-security-4 (4)spring security 认证和授权原理

    在上一节我们讨论了spring security过滤器的创建和注册原理.请记住springSecurityFilterChain(类型为FilterChainProxy)是实际起作用的过滤器链,Del ...

随机推荐

  1. HBase – 探索HFile索引机制

    本文由  网易云发布. 作者: 范欣欣 本篇文章仅限内部分享,如需转载,请联系网易获取授权. 01 HFile索引结构解析 HFile中索引结构根据索引层级的不同分为两种:single-level和m ...

  2. Delphi XE7的安卓程序如何调用JAVA的JAR,使用JAVA的类?

    本文使用工具和全部源码下载: http://download.csdn.net/detail/sunylat/8190765 为什么我们要在Delphi XE7的安卓程序调用JAVA的JAR,使用JA ...

  3. robot framework学习笔记之二———变量

    Robot Framework的变量分为标量, 列表和字典, 分别使用语法格式 ${SCALAR}, @{LIST} 和 &{DICT} 来定义. 此外, 环境变量可以直接使用语法 %{ENV ...

  4. HTML5语义化标签总结

    1.语义化标签总结 基础布局标签 <header></header> <nav></nav> <main></main> < ...

  5. Memcached安装教程及使用

    Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载 Table of contents 安装 使用 在spring中使用 安装 下载下来m ...

  6. QuantLib 金融计算——基本组件之 Index 类

    目录 QuantLib 金融计算--基本组件之 Index 类 QuantLib 金融计算--基本组件之 Index 类 Index 类用于表示已知的指数或者收益率,例如 Libor 或 Shibor ...

  7. 腾讯云服务器安装宝塔面板快速配置LNMP/LAMP网站系统

    我们在选择购买腾讯云服务器之后,有部分用户肯定是用来建站用途的.毕竟云服务器的性能和功能比虚拟主机优秀很多.腾讯云服务器拥有香港.北京.广州.上海.美国等多个机房,可以安装Linux和Windows系 ...

  8. 【GIS新探索】算法实现在不规则区域内均匀分布点

    1 概要 在不规则区域内均匀分布点,这个需求初看可能不好理解.如果设想一下需求场景就比较简单了. 场景1:在某个地区范围内,例如A市区有100W人口,需要将这100W人口在地图上面相对均匀的标识出来. ...

  9. 【实战】Axis2后台Getshell

    实战遇到的情况---任意文件读取,读取/conf/axis2.xml内容,读取用户名和密码登录后台 当然弱口令也是屡试不爽的. 操作起来 1.上传cat.aar(链接:https://pan.baid ...

  10. Android各版本及API对应关系,持续更新!

    以下是Android的各个版本与API的对应关系图标,便于查阅,会持续更新 API等级 Android版本号 Android版本名称 对应支持包 API等级1: Android 1.0     API ...