网络营销中网站建设的策略,做的网站乱码怎么搞,自己做的网站链接到微信支付界面,网站界面设计描述SpringSecurity登录认证和请求过滤器以及安全配置详解说明
环境 系统环境#xff1a;win10 Maven环境#xff1a;apache-maven-3.8.6 JDK版本#xff1a;1.8 SpringBoot版本#xff1a;2.7.8 根据用户名密码登录 根据用户名和密码登录#xff0c;登录成功后返回Token数据…
SpringSecurity登录认证和请求过滤器以及安全配置详解说明
环境 系统环境win10 Maven环境apache-maven-3.8.6 JDK版本1.8 SpringBoot版本2.7.8 根据用户名密码登录 根据用户名和密码登录登录成功后返回Token数据将token放到请求头中每次请求后台携带token数据 认证成功返回请求数据 携带token请求后台后台认证成功过滤器放行返回请求数据 认证失败SpringSecurity拦截请求 携带token请求后台后台认证失败请求被拦截 数据表结构 CREATE TABLE sys_user (id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 主键,user_name VARCHAR(64) NOT NULL DEFAULT NULL COMMENT 用户名,nick_name VARCHAR(64) NOT NULL DEFAULT NULL COMMENT 昵称,password VARCHAR(64) NOT NULL DEFAULT NULL COMMENT 密码,status CHAR(1) DEFAULT 0 COMMENT 账号状态0正常 1停用,email VARCHAR(64) DEFAULT NULL COMMENT 邮箱,phonenumber VARCHAR(32) DEFAULT NULL COMMENT 手机号,sex CHAR(1) DEFAULT NULL COMMENT 用户性别0男1女2未知,avatar VARCHAR(128) DEFAULT NULL COMMENT 头像,user_type CHAR(1) NOT NULL DEFAULT 1 COMMENT 用户类型0管理员1普通用户,create_by BIGINT(20) DEFAULT NULL COMMENT 创建人的用户id,create_time DATETIME DEFAULT NULL COMMENT 创建时间,update_by BIGINT(20) DEFAULT NULL COMMENT 更新人,update_time DATETIME DEFAULT NULL COMMENT 更新时间,del_flag INT(11) DEFAULT 0 COMMENT 删除标志0代表未删除1代表已删除,PRIMARY KEY (id)
) ENGINEINNODB AUTO_INCREMENT2 DEFAULT CHARSETutf8mb4 COMMENT用户表下面是本次Demo的项目代码和说明 项目环境依赖
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.7.8/versionrelativePath//parentgroupIdcn.molu.security.jwt/groupIdartifactIdSpringSecurity-JWT/artifactIdversion0.0.1-SNAPSHOT/versionnameSpringSecurity-JWT/namedescriptionSpringSecurity-JWT/descriptionpropertiesjava.version1.8/java.version/propertiesdependencies!--SpringSecurity安全框架--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId/dependency!--启用SpringBoot对Web的支持--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!--热部署插件--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-devtools/artifactIdscoperuntime/scopeoptionaltrue/optional/dependency!--Lombok实体类简化组件--dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependency!--lang3对象工具包--dependencygroupIdorg.apache.commons/groupIdartifactIdcommons-lang3/artifactId/dependency!--hutool工具包,数据加解密,对象判空转换等--dependencygroupIdcn.hutool/groupIdartifactIdhutool-all/artifactIdversion5.8.10/version/dependency!-- UA解析工具(从request中解析出访问设备信息) --dependencygroupIdeu.bitwalker/groupIdartifactIdUserAgentUtils/artifactIdversion1.21/version/dependency!--生成token依赖--dependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt/artifactIdversion0.9.1/version/dependency!-- MySQL数据连接驱动 --dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion8.0.27/version/dependency!--MyBatis-Plus操作数据库--dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.4.3/version/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactIdconfigurationexcludesexcludegroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId/exclude/excludes/configuration/plugin/plugins/build
/project项目启动入口 使用MyBatis-Plus操作数据库配置扫描mapper所在的包 SpringBootApplication
MapperScan(cn.molu.security.jwt.mapper)
public class SpringSecurityJwtApplication {public static void main(String[] args) {SpringApplication.run(SpringSecurityJwtApplication.class, args);}
}项目配置文件 MySQL地址、项目访问端口、token有效期 spring:# 数据库链接配置datasource:url: jdbc:mysql://127.0.0.1:3306/security?characterEncodingutf8serverTimezoneUTCusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driverapplication:name: SpringSecurity-JWT# 热部署devtools:restart:enabled: trueadditional-paths: src/main/java# 服务端口
server:port: 8090# 测试时将token有效期为5分钟
token:expire: 300000# 用于生成JWT的盐值
jwt:secret: 1234567890项目启动和关闭日志 项目启动和关闭时控制台打印相关提示信息 /*** ApiNote: 项目启动和关闭时的日志打印* Author: 陌路* Date: 2023/2/18 9:46* Tool: Created by IntelliJ IDEA*/
Slf4j
Component
public class AppStartAndStop implements ApplicationRunner, DisposableBean {Value(${server.port})private String port;/*** apiNote: 项目启动时运行此方法*/Overridepublic void run(ApplicationArguments args) {log.info(项目启动成功);log.info(请访问地址http://{}:{}, ApiUtils.getHostIp(), port);log.info();}/*** apiNote: 项目关闭时执行* return: void*/Overridepublic void destroy() {log.info();log.info(程序已停止运行);log.info();}
}封装统一响应实体类 统一返回给前台的数据实体 package cn.molu.security.jwt.vo;import cn.molu.security.jwt.utils.ApiUtils;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.NoArgsConstructor;
import lombok.ToString;import java.io.Serializable;
import java.util.HashMap;/*** ApiNote: 封装响应实体对象* Author: 陌路* Date: 2023/2/10 9:42* Tool: Created by IntelliJ IDEA.*/
NoArgsConstructor // 生成无参构造方法
ToString(callSuper true) // 重写toString方法
JsonInclude(JsonInclude.Include.NON_NULL)
public class ResultT extends HashMapString, Object implements Serializable {private static final long serialVersionUID 2637614641937282252L;// 返回结果数据public T result;// 返回成功失败标记public static Boolean flag;// 返回成功状态码public static final Integer SUCCESS 200;// 返回失败状态码public static final Integer FIELD 500;/*** apiNote: 返回数据* param: code 状态码 [返回给前台的状态码]* param: msg 提示消息 [返回给前台得消息]* param: result 响应数据结果[返回给前台得结果]* param: flag 响应标志[true成功false失败]* return: Result*/public static Result result(Integer code, String msg, Object result, Boolean flag) {Result r new Result();r.put(code, code);r.put(msg, msg);r.put(result, result);r.put(flag, flag);r.result result;Result.flag flag;return r;}/*** apiNote: 返回成功数据* param: msg 提示消息* param: result 响应数据结果* return: Result*/public static Result ok(Integer code, String msg, Object result) {return result(code, msg, result, true);}/*** apiNote: 返回成功数据* param: msg 提示消息* param: result 响应数据结果* return: Result*/public static Result ok(String msg, Object result) {return result(SUCCESS, msg, result, true);}/*** apiNote: 返回成功数据* param: result 响应数据结果* return: Result*/public static Result ok(Object result) {return result(SUCCESS, null, result, true);}/*** apiNote: 返回成功数据* param: msg 提示消息* return: Result*/public static Result ok(String msg) {return result(SUCCESS, msg, null, true);}/*** apiNote: 返回成功数据* return: Result*/public static Result ok() {return result(SUCCESS, null, null, true);}/*** apiNote: 返回失败数据* param: msg 错误消息* param: result 响应数据结果* return: Result*/public static Result err(Integer code, String msg, Object result) {return result(code, msg, result, false);}/*** apiNote: 返回失败数据* param: code 响应状态码* param: msg 错误消息* return: Result*/public static Result err(Integer code, String msg) {return result(code, msg, null, false);}/*** apiNote: 返回失败数据* param: msg 提示消息* param: result 响应数据结果* return: Result*/public static Result err(String msg, Object result) {return result(FIELD, msg, result, false);}/*** apiNote: 返回失败数据* param: result 响应数据结果* return: Result*/public static Result err(Object result) {return result(FIELD, null, result, false);}/*** apiNote: 返回失败数据* param: msg 错误消息* return: Result*/public static Result err(String msg) {return result(FIELD, msg, null, false);}/*** apiNote: 返回失败数据* return: Result*/public static Result err() {return result(FIELD, null, null, false);}/*** apiNote: 返回数据* param: [code, result, msg, flag]* return: cn.molu.api.vo.Result*/public static Result res(Integer code, Object result, String msg, boolean flag) {return result(code, msg, result, flag);}/*** apiNote: 返回数据* param: [flag, result]* return: cn.molu.api.vo.Result*/public static Result res(boolean flag, Object result) {return result(flag ? SUCCESS : FIELD, null, result, flag);}/*** apiNote: 返回数据* param: [flag, result]* return: cn.molu.api.vo.Result*/public static Result res(boolean flag, String msg, Object result) {return result(flag ? SUCCESS : FIELD, msg, result, flag);}/*** apiNote: 返回数据* param: [flag, msg]* return: cn.molu.api.vo.Result*/public static Result res(boolean flag, String msg) {return result(flag ? SUCCESS : FIELD, msg, null, flag);}/*** apiNote: 返回数据* param: [flag, msg]* return: cn.molu.api.vo.Result*/public static Result res(boolean flag) {return result(flag ? SUCCESS : FIELD, null, null, flag);}/*** apiNote: 重写HashMap的put方法* param: [key, value]* return: Result*/Overridepublic Result put(String key, Object value) {super.put(key, value);return this;}public T T getResult() {return ApiUtils.getObj(this.result, null);}public void setRes(boolean flag, T result) {this.flag flag;this.result result;put(flag, flag);put(result, result);}
}封装对象工具类 封装静态方法工具类便于在项目中使用 /*** ApiNote: api通用工具类* Author: 陌路* Date: 2023/2/10 9:26* Tool: Created by IntelliJ IDEA.*/
public class ApiUtils {/*** apiNote: 获取设备ip* return: String*/public static String getHostIp() {try {return InetAddress.getLocalHost().getHostAddress();} catch (UnknownHostException e) {return 127.0.0.1;}}/*** apiNote: 将对象转为字符串数据* param: [obj带转换对象]* return: java.lang.String*/public static String getStr(Object obj) {String str Objects.nonNull(obj) ? String.valueOf(obj).trim().replaceAll(\\s*|\r|\n|\t, ) : ;return null.equalsIgnoreCase(str) ? : str;}/*** apiNote: 将对象转为字符串数据, obj为空时返回defaultVal值* param: [obj, defaultVal]* return: java.lang.String*/public static String getStr(Object obj, String defaultVal) {final String str getStr(obj);return StringUtils.isBlank(str) ? defaultVal : str;}/*** apiNote: 当对象obj为空时返回defaultVal值* param: [obj, defaultVal]* return: java.lang.Object*/public static T T getObj(Object obj, Object defaultVal) {final String str getStr(obj);if (StringUtils.isBlank(str) ObjUtil.isNull(defaultVal)) {return null;}return (T) (StringUtils.isBlank(str) ? defaultVal : obj);}/*** apiNote: 校验数据是否为空* param: [msg, val]* return: void*/public static void hasText(String msg, Object... val) {if (ObjUtil.hasNull(val) || !ObjUtil.isAllNotEmpty(val) || val.length 0 || StringUtils.isBlank(getStr(val))) {Assert.hasText(null, msg);}}/*** apiNote: 向前台输出数据* param: [obj, response]* return: void*/public static void printJsonMsg(Object obj, HttpServletResponse response) {if (ObjUtil.isAllNotEmpty(obj, response)) {response.reset();response.setCharacterEncoding(utf-8);response.setContentType(application/json;charsetutf-8);try (final PrintWriter writer response.getWriter()) {writer.print(obj);writer.flush();} catch (IOException ignored) {}}}/*** apiNote: 校验数据是否未空为空则抛出异常* param: tipMsg异常提示信息* param: params需要校验的参数值*/public static void checkParamsIsEmpty(String tipMsg, Object... params) {if (ObjUtil.isNull(params) || !ObjUtil.isAllNotEmpty(params)) {throw new RuntimeException(getStr(tipMsg, 校验失败参数值为空));}}
}Token工具类 封装token工具类用于生成token和解析token数据 /*** ApiNote: token工具类* Author: 陌路* Date: 2023/02/10 16:00* Tool: Created by IntelliJ IDEA*/
Component
public class TokenUtils {Resourceprivate ContextLoader contextLoader;Value(${jwt.secret})private String secret;/*** apiNote: 生成token* param: userId 用户id* param: timeMillis 时间戳每次生成的Token都不一样* return: token*/public String createToken(Long userId, Long timeMillis) {ApiUtils.checkParamsIsEmpty(生成Token失败userId不能为空, userId);timeMillis timeMillis null ? System.currentTimeMillis() : timeMillis;String token Jwts.builder().claim(userId, userId).claim(timeMillis, timeMillis).signWith(SignatureAlgorithm.HS256, secret).compact();contextLoader.setCache(userId _KEY, token);return token;}/*** apiNote: 解析token数据* param: token* return: map*/public MapString, Object verifyToken(String token) {return StringUtils.isEmpty(token) ? new HashMap() : ApiUtils.getObj(Jwts.parser().setSigningKey(secret).parse(token).getBody(), new HashMap());}/*** apiNote: 根据token获取userId* param: token* return: userId*/public String getUserId(String token) {return ApiUtils.getStr(verifyToken(token).get(userId));}
}通过MyBatis-Plus操作数据库
/*** ApiNote: userMapper$* Author: 陌路* Date: 2023/2/18 11:13* Tool: Created by IntelliJ IDEA*/
Mapper
public interface UserMapper extends BaseMapperUser {}封装缓存工具类 封装数据缓存类用于缓存数据项目中使用redis做数据缓存 一般数据缓存是用redis来做的为了简便我这里就用了Map /*** ApiNote: 初始化缓存加载类* Author: 陌路* Date: 2023/2/10 9:29* Tool: Created by IntelliJ IDEA.* Desc: 正式开发中缓存数据应该放到redis中*/
Component
public class ContextLoader {// 缓存用户数据public static final MapString, LoginUser CACHE_USER new HashMap(2);// 缓存参数数据public static final MapString, Object CACHE_PARAM new HashMap(4);// 数据有效时长Value(${token.expire})private long expire;/*** apiNote: 根据token获取用户数据* param: [token]* return: cn.molu.api.pojo.User*/public LoginUser getCacheUser(String token) {if (StringUtils.isNotEmpty(token) CACHE_USER.containsKey(token)) {final LoginUser loginUser ApiUtils.getObj(CACHE_USER.get(token), new LoginUser());Long expire ApiUtils.getObj(loginUser.getExpire(), 0);long currentTimeMillis System.currentTimeMillis();if ((expire currentTimeMillis)) {if (expire - currentTimeMillis this.expire) {setCacheUser(token, loginUser);}return loginUser;}CACHE_USER.remove(token);}return new LoginUser();}/*** apiNote: 添加缓存数据到CACHE_USER中* param: [token, user]* return: cn.molu.api.pojo.User*/public void setCacheUser(String token, LoginUser loginUser) {if (StringUtils.isNotEmpty(token)) {loginUser.setExpire(System.currentTimeMillis() expire);CACHE_USER.put(token, loginUser);}}/*** apiNote: 向CACHE_PARAM中添加缓存数据* param: [key, val]* return: void*/public void setCache(String key, Object val) {if (StringUtils.isNotEmpty(key)) {CACHE_PARAM.put(key, val);}}/*** apiNote: 删除CACHE_USER中的用户数据* param: key* return: void*/public void deleteUser(String key) {if (StringUtils.isNotBlank(key) this.CACHE_USER.containsKey(key)) {this.CACHE_USER.remove(key);}}/*** apiNote: 删除CACHE_PARAM中的数据* param: key* return: void*/public void deleteParam(String key) {if (StringUtils.isNotEmpty(key) this.CACHE_PARAM.containsKey(key)) {this.CACHE_PARAM.remove(key);}}
}用户对象实体类 用户对象对应数据库中的sys_user表 /***ApiNote: 用户对象实体类对应数表sys_user*Author: 陌路*Date: 2023/2/18 20:46*Tool: Created by IntelliJ IDEA*/
Data
NoArgsConstructor
TableName(sys_user)
ToString(callSuper true)
JsonInclude(JsonInclude.Include.NON_EMPTY)
public class User implements Serializable {private static final long serialVersionUID -40356785423868312L;TableIdprivate Long id;//主键private String userName;//用户名private String nickName;//昵称private String password;//密码private String status;//账号状态0正常 1停用private String email;// 邮箱private String phone;//手机号private String sex;//用户性别0男1女2未知private String avatar;//头像private String userType;//用户类型0管理员1普通用户private Long createBy;//创建人的用户idprivate Date createTime;//创建时间private Long updateBy;//更新人private Date updateTime;//更新时间private Integer delFlag;//删除标志0代表未删除1代表已删除
}SpringSecurity核心内容
核心用户认证(登录) SpringSecurity登录业务需要实现SpringSecurity接口UserDetailsService中提供的方法loadUserByUsername并返回SpringSecurity提供的UserDetails接口对象 /*** ApiNote: 用户数据认证* Author: 陌路* Date: 2023/2/18 11:34* Tool: Created by IntelliJ IDEA*/
Service(userDetailsImpl)
public class UserDetailsImpl implements UserDetailsService {Resourceprivate UserMapper userMapper;/*** apiNote: 根据用户名获取用户数据* param: username 用户名* return: UserDetails*/Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 根据用户名查询用户数据User user userMapper.selectOne(new LambdaQueryWrapperUser().eq(User::getUserName, username));ApiUtils.checkParamsIsEmpty(未获取到用户数据请检查用户名和密码是否正确, user);// 根据用户信息查询相关权限// TODO: 权限相关配置后面实现目前先做认证 // 将用户数据封装到LoginUser中并返回return new LoginUser(user);}
}核心实现接口封装用户数据 SpringSecurity存储当前登录用户数据需要实现SpringSecurity提供的接口对象UserDetails通过LoginUser对象来接收loadUserByUsername返回的用户登录数据 /*** ApiNote: 封装登录用户数据* Author: 陌路* Date: 2023/2/18 11:55* Tool: Created by IntelliJ IDEA*/
Data
NoArgsConstructor
AllArgsConstructor
ToString(callSuper true)
JsonInclude(JsonInclude.Include.NON_NULL)
public class LoginUser implements UserDetails {// 实现SpringSecurity提供的UserDetails接口来管理用户数据private User user; // 用户数据对象private Long expire; // 过期时间private String token; // token// 构造方法public LoginUser(User user) {this.user user;}/*** apiNote: 获取当前登录用户信息*/public static LoginUser getLoginUser() {LoginUser loginUser (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();return ApiUtils.getObj(loginUser, new LoginUser());}/*** apiNote: 用户权限信息*/Overridepublic Collection? extends GrantedAuthority getAuthorities() {return null;}/*** apiNote: 获取用户密码*/Overridepublic String getPassword() {return user.getPassword();}/*** apiNote: 获取用户名*/Overridepublic String getUsername() {return user.getUserName();}/*** apiNote: 是否未过期(true未过期false已过期)*/Overridepublic boolean isAccountNonExpired() {return true;}/*** apiNote: 是否锁定*/Overridepublic boolean isAccountNonLocked() {return true;}/*** apiNote: 是否超时(true未超时false已超时)*/Overridepublic boolean isCredentialsNonExpired() {return true;}/*** apiNote: 当前用户是否可用true可用false不可用*/Overridepublic boolean isEnabled() {return true;}
}核心SpringSecurity配置类 SpringSecurity核心配置类用于配置自定义过滤器、拦截和放行用户请求 WebSecurityConfigurerAdapter此方法已过时可使用SecurityFilterChain来配置以下有说明 /*** ApiNote: SpringSecurity配置信息* Author: 陌路* Date: 2023/2/18 12:14* Tool: Created by IntelliJ IDEA*/
//Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {// 注入自定义的过滤器在用户名和密码认证之前执行UsernamePasswordAuthenticationFilter之前Resourceprivate TokenAuthorityFilter tokenAuthorityFilter;/*** apiNote: 注入密码加密工具*/Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}/*** apiNote: 注入AuthenticationManager对象来实现登录逻辑管理*/BeanOverrideprotected AuthenticationManager authenticationManager() throws Exception {return super.authenticationManager();}/*** apiNote: 配置请求认证和拦截*/Overrideprotected void configure(HttpSecurity http) throws Exception {// 关闭Security的CSRF功能防御http.csrf().disable()// 不通过Session获取SecurityContext.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 允许所有用户访问登录路径.antMatchers(/user/login).anonymous()//匿名访问未登录未认证的// 除以上请求路径外其他所有请求都必须经过认证才能访问成功.anyRequest().authenticated();// 添加自定义的请求过滤器tokenAuthorityFilter并定义在指定哪个过滤器UsernamePasswordAuthenticationFilter执行前执行http.addFilterBefore(tokenAuthorityFilter, UsernamePasswordAuthenticationFilter.class);}// 测试密码的加密和密码的验证public static void main(String[] args) {BCryptPasswordEncoder passwordEncoder new BCryptPasswordEncoder();// 加密后的密文每次加密结果都不一样因为加密时会生成随机盐值String encode passwordEncoder.encode(123456);// 校验用户输入的密码和加密后的密码是否一样一样返回true否则返回falseboolean matches passwordEncoder.matches(123456, encode);System.out.println(encode encode);System.out.println(matches matches);}
}以上对SpringSecurity配置的方法已过时 可以使用以下方法对SpringSecurity进行配置 /*** ApiNote: SpringSecurity配置信息* Author: 陌路* Date: 2023/2/18 12:14* Tool: Created by IntelliJ IDEA*/
Configuration
public class SecurityConfiguration {Resourceprivate TokenAuthorityFilter tokenAuthorityFilter;Resourceprivate AuthenticationConfiguration authenticationConfiguration;Beanpublic AuthenticationManager authenticationManager() throws Exception {AuthenticationManager authenticationManager authenticationConfiguration.getAuthenticationManager();return authenticationManager;}/*** apiNote: 注入密码加密工具*/Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 允许所有用户访问登录路径anonymous匿名访问即允许未登录时访问登录时则不允许访问.antMatchers(/user/login).anonymous()// 除以上请求路径外其他所有请求都必须经过认证才能访问成功.anyRequest().authenticated().and()// 添加自定义的请求过滤器tokenAuthorityFilter并定义在指定哪个过滤器UsernamePasswordAuthenticationFilter执行前执行.addFilterBefore(tokenAuthorityFilter, UsernamePasswordAuthenticationFilter.class);// 添加异常处理器http.exceptionHandling()// 认证异常处理器.authenticationEntryPoint(authenticationEntryPoint);// 运行跨域配置//http.cors();return http.build();}
}核心自定义请求过滤器 SpringSecurity自定义请求过滤器需要继承OncePerRequestFilter类并重写里面的doFilterInternal方法来实现具体的业务逻辑 /*** ApiNote: 请求过滤器是否认证是否有权访问* Author: 陌路* Date: 2023/2/18 13:04* Tool: Created by IntelliJ IDEA*/
Component
public class TokenAuthorityFilter extends OncePerRequestFilter {Resourceprivate TokenUtils tokenUtils;Resourceprivate ContextLoader contextLoader;/*** apiNote: 请求过滤器*/Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 获取token数据String authorityToken ApiUtils.getStr(request.getHeader(Authorization));// token为空直接放行if (StringUtils.isBlank(authorityToken)) {filterChain.doFilter(request, response);return;}// 解析token数据得到userIdString userId tokenUtils.getUserId(authorityToken);// 从缓存中获取用户信息LoginUser loginUser contextLoader.getCacheUser(userId _TOKEN_ authorityToken);ApiUtils.checkParamsIsEmpty(请求失败认证已过期, loginUser, loginUser.getUser());// 将用户信息封装到SecurityContextHolder中//principal用户数据credentialsauthenticated权限信息UsernamePasswordAuthenticationToken authenticationToken new UsernamePasswordAuthenticationToken(loginUser, null, null);SecurityContextHolder.getContext().setAuthentication(authenticationToken);filterChain.doFilter(request, response);}
}核心SpringSecurity异常处理 认证失败 实现SpringSecurity提供的AuthenticationEntryPoint接口中的commence方法来处理认证失败后的业务统一处理统一返回JSON异常提示信息在SpringSecurity配置类(SecurityConfiguration)中添加http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);即可 /*** ApiNote: 认证失败处理类* Author: 陌路* Date: 2023/2/19 12:25* Tool: Created by IntelliJ IDEA*/
Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {/*** apiNote: 认证失败处理* return: JSON认证失败请重新登录*/Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {String authExceptionMessage authException.getMessage();authExceptionMessage StringUtils.isBlank(ApiUtils.getStr(authExceptionMessage)) ? 认证失败请重新登录 : authExceptionMessage;String jsonStr JSONUtil.toJsonStr(Result.err(HttpStatus.UNAUTHORIZED.value(), authExceptionMessage));ApiUtils.printJsonMsg(jsonStr, response);}
}后台请求接口 用户请求后台接口登录接口、查询用户信息接口、注销登录接口 /*** ApiNote: 请求接口控制器* Author: 陌路* Date: 2023/2/18 9:53* Tool: Created by IntelliJ IDEA*/
RestController
RequestMapping(/user/*)
public class IndexController {Resourceprivate UserService userService;/*** apiNote: 获取用户列表* return: Result*/GetMapping(getUserList)public Result getUserList() {return Result.ok(userService.queryList());}/*** apiNote: 用户登录接口* param: User对象实体* return: Result*/PostMapping(login)public Result login(RequestBody User user) {return userService.login(user);}/*** apiNote: 用户退出登录* return: Result*/GetMapping(logout)public Result logout() {return Result.res(userService.logout());}
}请求接口实现类 用户请求接口实现类型登录、获取用户数据、注销登录 /*** ApiNote: userService$* Author: 陌路* Date: 2023/2/18 11:28* Tool: Created by IntelliJ IDEA*/
Service(userService)
public class UserServiceImpl implements UserService {Value(${token.expire})private long expire;Resourceprivate UserMapper userMapper;Resourceprivate TokenUtils tokenUtils;Resourceprivate ContextLoader contextLoader;Resourceprivate AuthenticationManager authenticationManager;/*** apiNote: 查询所有用户数据*/public ListUser queryList() {return userMapper.selectList(new LambdaQueryWrapperUser().eq(User::getDelFlag, 0));}/*** apiNote: 用户登录缓存用户数据* param: User* return: Result*/public Result login(User user) {UsernamePasswordAuthenticationToken authenticationToken new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());Authentication authenticate authenticationManager.authenticate(authenticationToken);ApiUtils.checkParamsIsEmpty(登录失败, authenticate);LoginUser loginUser ApiUtils.getObj(authenticate.getPrincipal(), new LoginUser());long currentTimeMillis System.currentTimeMillis();String token tokenUtils.createToken(loginUser.getUser().getId(), currentTimeMillis);loginUser.setToken(token);loginUser.setExpire(currentTimeMillis expire);contextLoader.setCacheUser(loginUser.getUser().getId() _TOKEN_ token, loginUser);return Result.ok(登录成功, token);}/*** apiNote: 用户退出登录删除用户缓存数据*/public boolean logout() {LoginUser loginUser LoginUser.getLoginUser();Long id loginUser.getUser().getId();String token loginUser.getToken();contextLoader.deleteUser(id _TOKEN_ token);contextLoader.deleteParam(id _KEY);return true;}
}项目接口调用实例 在请求体中输入用户名和密码进行登录登录时请求头不需要携带token请求/user/login接口登录成功 请求头中携带token请求/user/getUserList接口获取用户列表数据请求成功 请求头中携带token请求/user/logout接口退出登录请求成功 退出登录后携带toekn再次访问/user/getUserList获取用户列表接口可以看到请求被拒绝访问后台校验失败提示请求失败认证已过期 到此SpringSecurity登录认证部分已结束希望这篇文章对您有所帮助 下一篇SpringSecurity的权限校验
SpringSecurity的权限校验详解说明(附完整代码) https://blog.csdn.net/qq_51076413/article/details/129106824