1. 开启Security

@EnableWebSecurity
@Configuration
public class UserWebSecurityConfiguration extends WebSecurityConfigurerAdapter {@Resourceprivate PasswordEncoder passwordEncoder;@Resourceprivate LoginAuthenticationSuccessHandler successHandler;@Resourceprivate LoginAuthenticationFailureHandler failureHandler;/*** 请求网页未登录时抛出的异常信息*/@Resourceprivate MessageAuthenticationEntryPoint entryPoint;@Resourceprivate UserDetailsServiceImpl detailsService;@Resourceprivate UserLoginAuthorizationTokenFilter tokenFilter;private final String[] AUTH_WHITELIST = new String[]{"/user/auth/login", "/user/login", "/oauth/login"};//认证管理器@Override@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(detailsService).passwordEncoder(passwordEncoder);}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf(AbstractHttpConfigurer::disable).httpBasic(AbstractHttpConfigurer::disable).formLogin(n -> n.loginPage("/user/login").loginProcessingUrl("/user/auth/login").successHandler(successHandler).failureHandler(failureHandler)).logout(n -> n.logoutUrl("/user/auth/logout")).exceptionHandling(n->n.authenticationEntryPoint(entryPoint)).sessionManagement(n->n.sessionCreationPolicy(SessionCreationPolicy.STATELESS)).addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class).authorizeRequests(n -> n.antMatchers(AUTH_WHITELIST).permitAll().antMatchers("/user/api/**").authenticated());}@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/admin/**");}/*** 注册密码加密器** @return 密码加密器*/@Beanpublic PasswordEncoder bCryptPasswordEncoder() {return new BCryptPasswordEncoder();}
}

2. 配置登录验证UserDetailsService

package com.nineya.user.service;import com.nineya.tool.text.CheckText;
import com.nineya.user.dao.UserMapper;
import com.nineya.user.entity.NineyaUserDetails;
import com.nineya.user.entity.User;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.core.GrantedAuthority;
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;import javax.annotation.Resource;
import java.util.Collection;/*** @author 殇雪话诀别* 2020/11/22*/
@Component
public class UserDetailsServiceImpl implements UserDetailsService {@Resourceprivate UserMapper userMapper;@Resource@Lazyprivate PasswordEncoder passwordEncoder;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = new User();if (CheckText.checkPhone(username)) {user.setPhone(username);} else if (CheckText.checkMail(username)) {user.setMail(username);} else if (CheckText.checkNineyaId(username)) {user.setNineyaId(username);} else {throw new UsernameNotFoundException(String.format("账号 %s 格式有误!", username));}user = userMapper.getUser(user);if (user == null) {throw new UsernameNotFoundException(String.format("账号 %s 不存在", username));}user.setPassword(passwordEncoder.encode(user.getPassword()));return new NineyaUserDetails(user);}
}

3. 自定义登录成功和失败处理器

登录成功处理器,这里只是简单demo,记录一下,直接响应了authentication,这里可以通过JWT打印个token给客户端,等等…

  1. ResponseResult是自定义的REsult接口统一风格,和security无关,无须理会;
  2. adminTokenUtil为自己封装的token管理器,生成了一个jwt凭证,同时将token的id在Redis存底,如果id不存在该token就不在生效,token的有效期不在jwt中存放,由服务端控制,这样是为了防止密码丢失,token无法提前失效的情况;
  3. ObjectMapper是springboot的json转换工具,可以直接使用@Resource注入。
package com.nineya.user.handler;import com.fasterxml.jackson.databind.ObjectMapper;
import com.nineya.tool.restful.ResponseResult;
import com.nineya.user.entity.AdminDetails;
import com.nineya.user.util.AdminTokenUtil;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;/*** @author 殇雪话诀别* 2020/11/22*/
@Component
public class LoginAuthenticationSuccessHandler implements AuthenticationSuccessHandler {@Resourceprivate AdminTokenUtil adminTokenUtil;@Resourceprivate ObjectMapper mapper;@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {Map<String, Object> data = new HashMap<>();ResponseResult result = ResponseResult.access(data);response.setContentType("application/json;charset=UTF-8");AdminDetails details = (AdminDetails) authentication.getPrincipal();data.put("admin_token", adminTokenUtil.createToken(details.getAid()));response.getWriter().write(mapper.writeValueAsString(result));}
}

登录失败处理器:

package com.nineya.user.handler;import com.alibaba.fastjson.JSONObject;
import com.nineya.tool.restful.ResponseResult;
import com.nineya.tool.restful.ResultCode;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;/*** @author 殇雪话诀别* 2020/11/22*/
@Component
public class LoginAuthenticationFailureHandler implements AuthenticationFailureHandler {@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {ResponseResult result = new ResponseResult().setError(true);if (exception instanceof UsernameNotFoundException) {// 用户不存在result.setCode(ResultCode.SUCCESS.getCode());result.setMessage(exception.getMessage());} else if (exception instanceof BadCredentialsException) {// 密码错误result.setCode(ResultCode.SUCCESS.getCode());result.setMessage(exception.getMessage());} else if (exception instanceof LockedException) {// 用户被锁result.setCode(ResultCode.SUCCESS.getCode());result.setMessage(exception.getMessage());} else {// 系统错误result.setCode(ResultCode.SERVER_ERROR.getCode());result.setMessage(exception.getMessage());}response.setContentType("application/json;charset=UTF-8");response.getWriter().write(JSONObject.toJSONString(result));}
}

4. 创建访问失败处理器

访问接口时,因为登录失效,没有权限被访问等情况的处理器

package com.nineya.user.handler;import com.alibaba.fastjson.JSONObject;
import com.nineya.tool.restful.ResponseResult;
import com.nineya.tool.restful.ResultCode;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** @author 殇雪话诀别* 2020/11/25** 响应未登录消息*/
@Component
public class MessageAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {ResponseResult result = new ResponseResult(ResultCode.FORBIDDEN).setError(true);if (authException instanceof UsernameNotFoundException) {// 用户不存在result.setMessage(authException.getMessage());} else if (authException instanceof BadCredentialsException) {// 密码错误result.setMessage(authException.getMessage());} else if (authException instanceof LockedException) {// 用户被锁result.setMessage(authException.getMessage());} else  if (authException instanceof InsufficientAuthenticationException) {// 用户token失效result.setMessage("用户未登录");} else {// 系统错误result.setCode(ResultCode.SERVER_ERROR.getCode());result.setMessage(authException.getMessage());}response.setContentType("application/json;charset=UTF-8");response.getWriter().write(JSONObject.toJSONString(result));}
}

5. 创建token解析过滤器

SecurityContextHolder.getContext().setAuthentication(authentication);表示添加授权,仅对当前请求有效,每个被security拦截的请求都会执行该过滤器;

简单示例,使用时可通过token查询数据库、Redis,或者解析JWT等方式判断登录状态,然后决定是否添加authentication

package com.nineya.user.filter;import com.auth0.jwt.interfaces.DecodedJWT;
import com.nineya.tool.text.CheckText;
import com.nineya.user.entity.AdminDetails;
import com.nineya.user.util.AdminTokenUtil;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** @author 殇雪话诀别* 2020/11/26*/
@Component
public class AdminLoginAuthorizationTokenFilter extends OncePerRequestFilter {@Resourceprivate AdminTokenUtil adminTokenUtil;@Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {final String token = httpServletRequest.getHeader("Admin-Authorization");if (CheckText.isEmpty(token)) {filterChain.doFilter(httpServletRequest, httpServletResponse);return;}DecodedJWT decodedJWT = adminTokenUtil.verifyToken(token);long aid = decodedJWT.getClaim("aid").asLong();long createTime = decodedJWT.getIssuedAt().getTime();if (adminTokenUtil.valid(aid, createTime)) {AdminDetails adminDetails = new AdminDetails(aid);UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(adminDetails, null, adminDetails.getAuthorities());// 在上下文中记录UserDetailsSecurityContextHolder.getContext().setAuthentication(authentication);httpServletRequest.setAttribute("aid", aid);}filterChain.doFilter(httpServletRequest, httpServletResponse);}
}

Spring Security系列(一)——登录认证基本配置相关推荐

  1. 详解Spring Security的formLogin登录认证模式

    详解Spring Security的formLogin登录认证模式 一.formLogin的应用场景 在本专栏之前的文章中,已经给大家介绍过Spring Security的HttpBasic模式,该模 ...

  2. Spring Security使用数据库登录认证授权

    一.搭建项目环境 1.创建 RBAC五张表 RBAC,即基于角色的权限访问控制(Role-Based Access Control),就是用户通过角色与权限进行关联. 在这种模型中,用户与角色之间,角 ...

  3. Spring Security | 轻松搞定认证授权~

    文章目录 一.Spring Security 简介 二.整合 Spring Security 1.创建项目,导入依赖 2.创建数据库,搭建环境 3.配置数据库 4.创建实体类 5.实现 UserMap ...

  4. Spring Security系列之Spring Social实现微信社交登录(九)

    社交登录又称作社会化登录(Social Login),是指网站的用户可以使用腾讯QQ.人人网.开心网.新浪微博.搜狐微博.腾讯微博.淘宝.豆瓣.MSN.Google等社会化媒体账号登录该网站. 前言 ...

  5. Spring Security 3 Ajax登录–访问受保护的资源

    我看过一些有关Spring Security 3 Ajax登录的博客,但是我找不到解决如何调用基于Ajax的登录的博客,匿名用户正在Ajax中访问受保护的资源. 问题 – Web应用程序允许匿名访问某 ...

  6. spring security系列一:架构概述

    一直以来都想好好写一写spring security 系列文章,每每提笔又不知何处下笔,又赖于spring security体系强大又过于繁杂,且spring security 与auth2.0结合的 ...

  7. Spring Security系列教程03--创建SpringSecurity项目

    前言 在上一章节中,一一哥 已经带大家认识了Spring Security,对其基本概念已有所了解,但是作为一个合格的程序员,最关键的肯定还是得动起手来,所以从本篇文章开始,我就带大家搭建第一个Spr ...

  8. Spring Security系列教程11--Spring Security核心API讲解

    前言 经过前面几个章节的学习,一一哥 带大家实现了基于内存和数据库模型的认证与授权,尤其是基于自定义的数据库模型更是可以帮助我们进行灵活开发.但是前面章节的内容,属于让我们达到了 "会用&q ...

  9. Spring Security系列教程-Spring Security核心API讲解

    前言 经过前面几个章节的学习,一一哥 带大家实现了基于内存和数据库模型的认证与授权,尤其是基于自定义的数据库模型更是可以帮助我们进行灵活开发.但是前面章节的内容,属于让我们达到了 "会用&q ...

  10. Spring Security系列教程18--会话管理之防御固定会话攻击

    前言 在前面几个章节中,一一哥 带各位学习了如何实现基于数据库进行认证授权,如何给登录界面添加图形验证码,如何进行自动登录和注销登录,那么Spring Security的功能难道只有这些吗?肯定不是的 ...

最新文章

  1. vs快捷键及常用设置(vs2012版)
  2. python编程视频-【科研资源03】最全Python编程全套系统视频学习教程
  3. java弹出提示窗口_Java实现弹窗效果的基本操作(2)
  4. 键盘录入,替换敏感词
  5. Leetcode--128. 最长连续序列
  6. mysql mos login_MySQL 中常用的函数
  7. 等保2.0标准发布一周年,行业用户如何有效落实合规建设
  8. VB如何实现Undo(撤消)功能
  9. C#:导出json数据到Excel表格
  10. MySQL常用的日期时间函数
  11. dw01均衡电路_基于DW01芯片的锂电池保护电路设计
  12. NK细胞培养方法与优化解决方案
  13. day09 文件操作相关
  14. debian/ubuntu 64bit 安装 android sdk时adb无法编译的问题
  15. linux 算术命令,shell中进行算术运算的各种方法
  16. c语言如何画简单图形,如何用C语言画基本图形
  17. 循环神经网络中的LSTM和GRU
  18. On children
  19. 《Head First jQuery》读书笔记
  20. 57步进电机了解与实践笔记

热门文章

  1. openwrt在mt7620a上的折腾笔记
  2. 300句子与7000单词
  3. kali linux怎么安装无线网卡驱动,Kali Linux 安装BCM43142网卡驱动
  4. c语言中calloc函数,C 库函数 – calloc()
  5. 10个小窍门,让你轻松准确搜索(转)
  6. Pycharm typo PEP 8
  7. 【华为OD机试Python实现】HJ67 24点游戏算法(中等)
  8. 阿里面试:设计一个电商平台积分兑换系统!
  9. Widows系统截屏工具
  10. Arduino六足机器人