news 2026/6/11 3:07:02

返利公众号的安全防护体系:Java Spring Security OAuth2+JWT实现用户身份的多端统一认证

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
返利公众号的安全防护体系:Java Spring Security OAuth2+JWT实现用户身份的多端统一认证

返利公众号的安全防护体系:Java Spring Security OAuth2+JWT实现用户身份的多端统一认证

大家好,我是 微赚淘客系统3.0 的研发者省赚客!

在返利类公众号场景中,用户可能通过微信小程序、H5页面或App访问同一套后端服务。为保障账户安全并实现“一次登录、多端通行”,我们基于 Spring Security + OAuth2 + JWT 构建了统一认证中心。本文将聚焦 Java 实现细节,展示如何通过自定义授权服务器、资源服务器及 Token 管理机制,完成高安全性的多端身份认证。

整体架构设计

系统采用 OAuth2 的密码模式(Resource Owner Password Credentials)结合自定义扩展,适用于自有客户端。JWT 作为 Token 载体,包含用户ID、角色、设备类型等信息,并通过 HS512 签名防篡改。所有敏感接口均受 Spring Security 保护。

JWT 工具类与 Token 生成

首先定义 JWT 工具类,用于签发和解析 Token:

packagejuwatech.cn.security.jwt;importio.jsonwebtoken.Claims;importio.jsonwebtoken.Jwts;importio.jsonwebtoken.SignatureAlgorithm;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.stereotype.Component;importjava.util.Date;importjava.util.HashMap;importjava.util.Map;@ComponentpublicclassJwtTokenUtil{@Value("${jwt.secret:juwatech-secret-key-2026}")privateStringsecret;@Value("${jwt.expiration:86400}")// 24小时privateLongexpiration;publicStringgenerateToken(StringuserId,StringclientId){Map<String,Object>claims=newHashMap<>();claims.put("clientId",clientId);claims.put("userId",userId);returnJwts.builder().setClaims(claims).setIssuedAt(newDate()).setExpiration(newDate(System.currentTimeMillis()+expiration*1000)).signWith(SignatureAlgorithm.HS512,secret).compact();}publicClaimsgetClaimsFromToken(Stringtoken){returnJwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();}publicbooleanvalidateToken(Stringtoken){try{Claimsclaims=getClaimsFromToken(token);return!claims.getExpiration().before(newDate());}catch(Exceptione){returnfalse;}}}

自定义 UserDetailsService

实现UserDetailsService从数据库加载用户信息:

packagejuwatech.cn.security.service;importjuwatech.cn.mapper.UserMapper;importjuwatech.cn.model.User;importorg.springframework.security.core.userdetails.UserDetails;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.core.userdetails.UsernameNotFoundException;importorg.springframework.stereotype.Service;@ServicepublicclassCustomUserDetailsServiceimplementsUserDetailsService{privatefinalUserMapperuserMapper;publicCustomUserDetailsService(UserMapperuserMapper){this.userMapper=userMapper;}@OverridepublicUserDetailsloadUserByUsername(Stringmobile)throwsUsernameNotFoundException{Useruser=userMapper.selectByMobile(mobile);if(user==null){thrownewUsernameNotFoundException("User not found: "+mobile);}returnorg.springframework.security.core.userdetails.User.withUsername(user.getMobile()).password(user.getPassword())// 已加密存储.authorities("ROLE_USER").accountExpired(false).accountLocked(false).credentialsExpired(false).disabled(false).build();}}

OAuth2 授权服务器配置

配置授权服务器,支持密码模式并集成 JWT:

packagejuwatech.cn.security.oauth2;importjuwatech.cn.security.jwt.JwtTokenUtil;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.authentication.AuthenticationManager;importorg.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;importorg.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;importorg.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;importorg.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;importorg.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;importorg.springframework.security.oauth2.provider.token.TokenStore;importorg.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;importorg.springframework.security.oauth2.provider.token.store.JwtTokenStore;@Configuration@EnableAuthorizationServerpublicclassAuthorizationServerConfigextendsAuthorizationServerConfigurerAdapter{privatefinalAuthenticationManagerauthenticationManager;privatefinalJwtTokenUtiljwtTokenUtil;publicAuthorizationServerConfig(AuthenticationManagerauthenticationManager,JwtTokenUtiljwtTokenUtil){this.authenticationManager=authenticationManager;this.jwtTokenUtil=jwtTokenUtil;}@Overridepublicvoidconfigure(ClientDetailsServiceConfigurerclients)throwsException{clients.inMemory().withClient("wechat-miniprogram").secret("{noop}mp-secret-2026").authorizedGrantTypes("password","refresh_token").scopes("read","write").accessTokenValiditySeconds(86400).refreshTokenValiditySeconds(604800);}@BeanpublicTokenStoretokenStore(){returnnewJwtTokenStore(jwtAccessTokenConverter());}@BeanpublicJwtAccessTokenConverterjwtAccessTokenConverter(){JwtAccessTokenConverterconverter=newJwtAccessTokenConverter();converter.setSigningKey("juwatech-secret-key-2026");returnconverter;}@Overridepublicvoidconfigure(AuthorizationServerEndpointsConfigurerendpoints){endpoints.tokenStore(tokenStore()).accessTokenConverter(jwtAccessTokenConverter()).authenticationManager(authenticationManager);}@Overridepublicvoidconfigure(AuthorizationServerSecurityConfigurersecurity){security.allowFormAuthenticationForClients().checkTokenAccess("isAuthenticated()");}}

资源服务器与权限校验

在资源服务器中解析 JWT 并构建 SecurityContext:

packagejuwatech.cn.security.resource;importjuwatech.cn.security.jwt.JwtTokenUtil;importorg.springframework.security.authentication.UsernamePasswordAuthenticationToken;importorg.springframework.security.core.context.SecurityContextHolder;importorg.springframework.security.core.userdetails.UserDetails;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.stereotype.Component;importorg.springframework.web.filter.OncePerRequestFilter;importjavax.servlet.FilterChain;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;@ComponentpublicclassJwtAuthenticationFilterextendsOncePerRequestFilter{privatefinalJwtTokenUtiljwtTokenUtil;privatefinalUserDetailsServiceuserDetailsService;publicJwtAuthenticationFilter(JwtTokenUtiljwtTokenUtil,UserDetailsServiceuserDetailsService){this.jwtTokenUtil=jwtTokenUtil;this.userDetailsService=userDetailsService;}@OverrideprotectedvoiddoFilterInternal(HttpServletRequestrequest,HttpServletResponseresponse,FilterChainchain)throwsServletException,IOException{StringauthHeader=request.getHeader("Authorization");if(authHeader!=null&&authHeader.startsWith("Bearer ")){Stringtoken=authHeader.substring(7);if(jwtTokenUtil.validateToken(token)){Stringmobile=jwtTokenUtil.getClaimsFromToken(token).getSubject();UserDetailsuserDetails=userDetailsService.loadUserByUsername(mobile);UsernamePasswordAuthenticationTokenauthentication=newUsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());SecurityContextHolder.getContext().setAuthentication(authentication);}}chain.doFilter(request,response);}}

并在 WebSecurity 中启用:

packagejuwatech.cn.security.config;importjuwatech.cn.security.resource.JwtAuthenticationFilter;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.EnableWebSecurity;importorg.springframework.security.config.http.SessionCreationPolicy;importorg.springframework.security.web.SecurityFilterChain;importorg.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration@EnableWebSecuritypublicclassResourceServerConfig{privatefinalJwtAuthenticationFilterjwtAuthenticationFilter;publicResourceServerConfig(JwtAuthenticationFilterjwtAuthenticationFilter){this.jwtAuthenticationFilter=jwtAuthenticationFilter;}@BeanpublicSecurityFilterChainfilterChain(HttpSecurityhttp)throwsException{http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers("/api/auth/**").permitAll().antMatchers("/api/refund/**").hasRole("USER").anyRequest().authenticated().and().addFilterBefore(jwtAuthenticationFilter,UsernamePasswordAuthenticationFilter.class);returnhttp.build();}}

多端统一登录接口

提供统一登录入口,适配不同客户端:

packagejuwatech.cn.controller;importjuwatech.cn.security.jwt.JwtTokenUtil;importorg.springframework.security.authentication.AuthenticationManager;importorg.springframework.security.authentication.UsernamePasswordAuthenticationToken;importorg.springframework.web.bind.annotation.PostMapping;importorg.springframework.web.bind.annotation.RequestBody;importorg.springframework.web.bind.annotation.RestController;@RestControllerpublicclassAuthController{privatefinalAuthenticationManagerauthenticationManager;privatefinalJwtTokenUtiljwtTokenUtil;publicAuthController(AuthenticationManagerauthenticationManager,JwtTokenUtiljwtTokenUtil){this.authenticationManager=authenticationManager;this.jwtTokenUtil=jwtTokenUtil;}@PostMapping("/api/auth/login")publicObjectlogin(@RequestBodyLoginRequestreq){authenticationManager.authenticate(newUsernamePasswordAuthenticationToken(req.getMobile(),req.getPassword()));Stringtoken=jwtTokenUtil.generateToken(req.getMobile(),req.getClientId());returnjava.util.Map.of("access_token",token,"token_type","Bearer");}publicstaticclassLoginRequest{privateStringmobile;privateStringpassword;privateStringclientId;// getters and setterspublicStringgetMobile(){returnmobile;}publicvoidsetMobile(Stringmobile){this.mobile=mobile;}publicStringgetPassword(){returnpassword;}publicvoidsetPassword(Stringpassword){this.password=password;}publicStringgetClientId(){returnclientId;}publicvoidsetClientId(StringclientId){this.clientId=clientId;}}}

本文著作权归 微赚淘客系统3.0 研发团队,转载请注明出处!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 12:32:43

Midjourney:还在连连看?“AI 材质流” 3分钟量产 8K 级 PBR 贴图

对于游戏场景/3D 艺术家来说&#xff0c;“搓材质” 既是技术活也是体力活。 想做一个复杂的“长满藤蔓的湿润石墙”&#xff0c;在 Substance Designer 里你得连几百个节点&#xff0c;调噪点、混合高度图、解算 AO&#xff0c;稍微改个参数就得等半天解算。去找素材网的现成贴…

作者头像 李华
网站建设 2026/6/10 12:39:57

【小程序毕设全套源码+文档】基于微信小程序的助农农产品销售平台设计与实现(丰富项目+远程调试+讲解+定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/6/10 12:41:22

使用 vLLM 部署 GLM-4.7-Flash 大模型完整指南

前言 GLM-4.7-Flash 是智谱 AI 推出的一款高性能大语言模型。本文将详细介绍如何在 Ubuntu 22.04 CUDA 12.4 环境下&#xff0c;使用 vLLM 框架部署 GLM-4.7-Flash 模型&#xff0c;支持双卡推理和工具调用功能。 环境准备 硬件要求 GPU: 2x NVIDIA L20 48GB内存: 至少 128GB存…

作者头像 李华
网站建设 2026/6/10 12:29:53

瑞维美尼Revumenib分化综合征的预防,如何使用皮质类固醇预处理?

分化综合征&#xff08;DS&#xff09;是瑞维美尼治疗中最需警惕的致命并发症&#xff0c;其发生机制与白血病细胞快速分化相关。AUGMENT-101试验显示&#xff0c;约29%的患者出现DS&#xff0c;症状包括发热、呼吸困难、低氧血症、外周水肿、胸膜或心包积液&#xff0c;严重者…

作者头像 李华
网站建设 2026/6/10 21:11:05

论文投出去好几个月都没动静,可以催稿吗?【附模板】

投稿后等待审稿的焦虑&#xff0c;每个学术研究者都懂&#xff1a;想催稿又怕冒犯编辑&#xff0c;不催又满心忐忑&#xff0c;连找个合适的模板都要纠结半天。 “投稿3个月没动静&#xff0c;该不该问&#xff1f;” “催急了会不会被拒稿&#xff1f;” 这些焦虑大可不必&…

作者头像 李华
网站建设 2026/6/10 20:38:46

丝氨酸/苏氨酸磷酸化抗体在蛋白质合成研究中发挥何种作用?

一、蛋白质化学合成为何需要引入特定磷酸化修饰&#xff1f;蛋白质的化学合成技术能够在任意指定位置引入精确设计的翻译后修饰&#xff0c;这一特性使其在生命科学研究中具有不可替代的重要价值。特别是在蛋白质功能调控研究中&#xff0c;丝氨酸和苏氨酸残基的磷酸化修饰作为…

作者头像 李华