Spring Security系列(一)——登录认证基本配置
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
给客户端,等等…
ResponseResult
是自定义的REsult接口统一风格,和security无关,无须理会;adminTokenUtil
为自己封装的token管理器,生成了一个jwt凭证,同时将token的id在Redis存底,如果id不存在该token就不在生效,token的有效期不在jwt中存放,由服务端控制,这样是为了防止密码丢失,token无法提前失效的情况;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系列(一)——登录认证基本配置相关推荐
- 详解Spring Security的formLogin登录认证模式
详解Spring Security的formLogin登录认证模式 一.formLogin的应用场景 在本专栏之前的文章中,已经给大家介绍过Spring Security的HttpBasic模式,该模 ...
- Spring Security使用数据库登录认证授权
一.搭建项目环境 1.创建 RBAC五张表 RBAC,即基于角色的权限访问控制(Role-Based Access Control),就是用户通过角色与权限进行关联. 在这种模型中,用户与角色之间,角 ...
- Spring Security | 轻松搞定认证授权~
文章目录 一.Spring Security 简介 二.整合 Spring Security 1.创建项目,导入依赖 2.创建数据库,搭建环境 3.配置数据库 4.创建实体类 5.实现 UserMap ...
- Spring Security系列之Spring Social实现微信社交登录(九)
社交登录又称作社会化登录(Social Login),是指网站的用户可以使用腾讯QQ.人人网.开心网.新浪微博.搜狐微博.腾讯微博.淘宝.豆瓣.MSN.Google等社会化媒体账号登录该网站. 前言 ...
- Spring Security 3 Ajax登录–访问受保护的资源
我看过一些有关Spring Security 3 Ajax登录的博客,但是我找不到解决如何调用基于Ajax的登录的博客,匿名用户正在Ajax中访问受保护的资源. 问题 – Web应用程序允许匿名访问某 ...
- spring security系列一:架构概述
一直以来都想好好写一写spring security 系列文章,每每提笔又不知何处下笔,又赖于spring security体系强大又过于繁杂,且spring security 与auth2.0结合的 ...
- Spring Security系列教程03--创建SpringSecurity项目
前言 在上一章节中,一一哥 已经带大家认识了Spring Security,对其基本概念已有所了解,但是作为一个合格的程序员,最关键的肯定还是得动起手来,所以从本篇文章开始,我就带大家搭建第一个Spr ...
- Spring Security系列教程11--Spring Security核心API讲解
前言 经过前面几个章节的学习,一一哥 带大家实现了基于内存和数据库模型的认证与授权,尤其是基于自定义的数据库模型更是可以帮助我们进行灵活开发.但是前面章节的内容,属于让我们达到了 "会用&q ...
- Spring Security系列教程-Spring Security核心API讲解
前言 经过前面几个章节的学习,一一哥 带大家实现了基于内存和数据库模型的认证与授权,尤其是基于自定义的数据库模型更是可以帮助我们进行灵活开发.但是前面章节的内容,属于让我们达到了 "会用&q ...
- Spring Security系列教程18--会话管理之防御固定会话攻击
前言 在前面几个章节中,一一哥 带各位学习了如何实现基于数据库进行认证授权,如何给登录界面添加图形验证码,如何进行自动登录和注销登录,那么Spring Security的功能难道只有这些吗?肯定不是的 ...
最新文章
- vs快捷键及常用设置(vs2012版)
- python编程视频-【科研资源03】最全Python编程全套系统视频学习教程
- java弹出提示窗口_Java实现弹窗效果的基本操作(2)
- 键盘录入,替换敏感词
- Leetcode--128. 最长连续序列
- mysql mos login_MySQL 中常用的函数
- 等保2.0标准发布一周年,行业用户如何有效落实合规建设
- VB如何实现Undo(撤消)功能
- C#:导出json数据到Excel表格
- MySQL常用的日期时间函数
- dw01均衡电路_基于DW01芯片的锂电池保护电路设计
- NK细胞培养方法与优化解决方案
- day09 文件操作相关
- debian/ubuntu 64bit 安装 android sdk时adb无法编译的问题
- linux 算术命令,shell中进行算术运算的各种方法
- c语言如何画简单图形,如何用C语言画基本图形
- 循环神经网络中的LSTM和GRU
- On children
- 《Head First jQuery》读书笔记
- 57步进电机了解与实践笔记