当前位置: 首页 > news >正文

哪里可以做网站啊营销咨询公司排名前十

哪里可以做网站啊,营销咨询公司排名前十,东莞网站快速排名,wordpress评论积分作者:后端小肥肠 创作不易,未经允许严禁转载。 姊妹篇: 【Spring Security系列】Spring SecurityJWTRedis实现用户认证登录及登出_spring security jwt 退出登录-CSDN博客 1. 前言 欢迎来到【Spring Security系列】!在当今数字化…

作者:后端小肥肠

创作不易,未经允许严禁转载。

姊妹篇:

【Spring Security系列】Spring Security+JWT+Redis实现用户认证登录及登出_spring security jwt 退出登录-CSDN博客

1. 前言

欢迎来到【Spring Security系列】!在当今数字化时代,安全是任何应用程序都必须优先考虑的核心问题之一。而在众多安全框架中,Spring Security 作为一个功能强大且广泛应用的安全框架,为 Java 应用程序提供了全面的身份验证、授权、攻击防护等功能。而随着移动应用的普及,小程序作为一种轻量级、跨平台的应用形式,其安全性也成为了开发者们关注的焦点。本文将带领您深入探索如何使用 Spring Security 来保护小程序的登录认证,旨在为您提供全方位的学习体验和实践指导。

2. 小程序登录涉及SpringSecurity核心组件介绍

如果要在SpringSecurity默认的用户名密码模式登录模式上扩展小程序登录,涉及到的核心组件如下:

  1. AuthenticationProvider

    创建自定义的AuthenticationProvider,负责处理从微信开放平台获取的用户信息,并进行身份验证。
  2. UserDetailsService

    调整UserDetailsService来获取并管理基于微信OpenID的用户信息。
  3. AuthenticationManager

    确保您的自定义AuthenticationProvider被正确注册到AuthenticationManager中,以便处理小程序登录请求。
  4. SecurityConfigurer

    创建一个SecurityConfigurer来配置Spring Security以支持小程序登录,并将其添加到Spring Security的配置类中。
  5. Filter

    创建一个自定义的过滤器来拦截和处理小程序登录请求,提取微信登录凭证,并将其传递给您的自定义AuthenticationProvider进行处理。

要扩展Spring Security以支持小程序登录,您需要创建自定义的AuthenticationProvider并调整UserDetailsService以处理微信OpenID的用户信息。

3. SpringSecurity集成小程序登录原理

3.1. 小程序登录流程

以下是微信官方文档中小程序登录的流程:

由上图可看出,小程序登录使用微信提供的登录凭证 code,通过微信开放平台的接口获取用户的唯一标识 OpenID 和会话密钥 SessionKey。在集成小程序登录时,我们需要将这些凭证传递给后端服务器,由后端服务器进行校验和处理,最终完成用户的登录认证。

3.2. SpringSecurity集成小程序登录流程梳理

结合SpringSecurity原理,在SpringSecurity中集成小程序登录的流程如下:

  • 小程序端:通过微信登录接口获取登录凭证 code,这里取名为loginCode。
  • 小程序端,通过手机号快速验证组件获取phoneCode。
  • 小程序端,获取用户昵称(nickName)和用户头像(imageUrl)地址。
  • 小程序端:将登录凭证 loginCodephoneCodenickNameimageUrl发送给后端服务器。
  • 后端服务器:接收到登录凭证 loginCode后,调用微信开放平台的接口,换取用户的唯一标识 OpenID 和会话密钥 SessionKey
  • 后端服务器:根据 OpenID 查询用户信息,如果用户不存在,则创建新用户;如果用户已存在,则返回用户信息。
  • 后端服务器:生成用户的身份认证信息JWT Token,返回给小程序端。
  • 小程序端:存储用户的身份认证信息,后续请求携带该信息进行访问控制。

大体流程只是在3.1小程序登录流程上做了细化,图我就不画了(因为懒)。

3.3. 小程序登录接口设计

小程序登录接口如下图所示:

由上图所示,我们需要传入loginCode,phoneCode(获取手机号),nickName(昵称用于登录后展示),imageUrl(头像用于登录后展示)这几个必传参数。

4. 核心代码讲解

4.1. 小程序端获取必要参数

1. 小程序端调用微信登录接口,获取用户登录凭证 loginCode。

wx.login({success (res) {if (res.code) {//发起网络请求wx.request({url: 'https://example.com/onLogin',data: {code: res.code}})} else {console.log('登录失败!' + res.errMsg)}}
})

2. 获取PhoneCode

3. 获取头像昵称

4.2. 编写WeChatAuthenticationFilter

public class WeChatAuthenticationFilter extends AbstractAuthenticationProcessingFilter {private final String loginCode = "loginCode";private final String phoneCode="phoneCode";private final String nickName="nickName";private final String imageUrl="imageUrl";public WeChatAuthenticationFilter(String appId, String secret) {super(new AntPathRequestMatcher("/wx/login", "POST"));}@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {String loginCode = obtainLoginCode(request)==null?"":obtainLoginCode(request).trim();String phoneCode=obtainPhoneCode(request)==null?"":obtainPhoneCode(request).trim();String nickName=obtainNickName(request)==null?"":obtainNickName(request).trim();String imageUrl=obtainImageUrl(request)==null?"":obtainImageUrl(request).trim();WechatAuthenticationToken authRequest = new WechatAuthenticationToken(loginCode,phoneCode,nickName,imageUrl);return this.getAuthenticationManager().authenticate(authRequest);}protected String obtainLoginCode(HttpServletRequest request) {return request.getParameter(loginCode);}protected String obtainPhoneCode(HttpServletRequest request){return request.getParameter(phoneCode);}protected String obtainNickName(HttpServletRequest request){return request.getParameter(nickName);}protected String obtainImageUrl(HttpServletRequest request){return request.getParameter(imageUrl);}
}

以上代码定义了一个名为WeChatAuthenticationFilter的类,它继承自AbstractAuthenticationProcessingFilter类,用于处理微信登录认证。在构造函数中,指定了请求匹配路径为"/wx/login",请求方法为POST。类中定义了四个常量:loginCode、phoneCode、nickName和imageUrl,分别表示登录码、手机号码、昵称和头像URL。

attemptAuthentication方法中,首先通过obtainLoginCode、obtainPhoneCode、obtainNickName和obtainImageUrl方法获取请求中的登录码、手机号码、昵称和头像URL,并进行了空值处理。然后将这些信息封装到WechatAuthenticationToken对象中,并通过getAuthenticationManager().authenticate方法进行认证。

4.3. 编写WeChatAuthenticationProvider

@Slf4j
public class WeChatAuthenticationProvider implements AuthenticationProvider {private final WechatConfig wechatConfig;private  RestTemplate restTemplate;private final WeChatService weChatService;private final ISysUserAuthService sysUserAuthService;public WeChatAuthenticationProvider(WechatConfig wechatConfig, WeChatService weChatService,ISysUserAuthService sysUserAuthService) {this.wechatConfig = wechatConfig;this.weChatService = weChatService;this.sysUserAuthService=sysUserAuthService;}@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {WechatAuthenticationToken wechatAuthenticationToken = (WechatAuthenticationToken) authentication;String loginCode = wechatAuthenticationToken.getPrincipal().toString();log.info("loginCode is {}",loginCode);String phoneCode=wechatAuthenticationToken.getPhoneCode().toString();log.info("phoneCode is {}",phoneCode);String nickName=wechatAuthenticationToken.getNickName().toString();log.info("nickName is {}",nickName);String imageUrl=wechatAuthenticationToken.getImageUrl().toString();log.info("imageUrl is {}",imageUrl);restTemplate=new RestTemplate();//获取openIdJwtUser jwtUser=null;String url = "https://api.weixin.qq.com/sns/jscode2session?appid={appid}&secret={secret}&js_code={code}&grant_type=authorization_code";Map<String, String> requestMap = new HashMap<>();requestMap.put("appid", wechatConfig.getAppid());requestMap.put("secret", wechatConfig.getSecret());requestMap.put("code", loginCode);ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class,requestMap);JSONObject jsonObject= JSONObject.parseObject(responseEntity.getBody());log.info(JSONObject.toJSONString(jsonObject));String openId=jsonObject.getString("openid");if(StringUtils.isBlank(openId)) {throw new BadCredentialsException("weChat get openId error");}if(sysUserAuthService.getUserAuthCountByIdentifier(openId)>0){jwtUser = (JwtUser) weChatService.getUserByOpenId(openId);if(!jwtUser.isEnabled()){throw new BadCredentialsException("用户已失效");}return getauthenticationToken(jwtUser,jwtUser.getAuthorities());}//获取手机号第一步,获取accessTokenString accessTokenUrl="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}";Map<String, String> accessTokenRequestMap = new HashMap<>();accessTokenRequestMap.put("appid", wechatConfig.getAppid());accessTokenRequestMap.put("secret", wechatConfig.getSecret());ResponseEntity<String>  accessTokenResponseEntity = restTemplate.getForEntity(accessTokenUrl, String.class,accessTokenRequestMap);JSONObject  accessTokenJsonObject= JSONObject.parseObject(accessTokenResponseEntity.getBody());log.info(JSONObject.toJSONString(accessTokenJsonObject));String  accessToken=accessTokenJsonObject.getString("access_token");if(StringUtils.isBlank(accessToken)) {throw new BadCredentialsException("weChat get accessToken error");}//获取手机号第二部,远程请求获取手机号String pohoneUrl="https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token="+accessToken+"";JSONObject phoneJson=new JSONObject();phoneJson.put("code",phoneCode);String resPhoneStr= RestTemplateUtil.postForJson(pohoneUrl,phoneJson,restTemplate);log.info(resPhoneStr);JSONObject resPhonJson= JSON.parseObject(resPhoneStr);JSONObject phoneInfo=resPhonJson.getJSONObject("phone_info");String mobile=phoneInfo.getString("phoneNumber");if(StringUtils.isBlank(mobile)){throw new BadCredentialsException("Wechat get mobile error");}jwtUser= (JwtUser) weChatService.getUserByMobile(mobile,nickName,imageUrl);sysUserAuthService.saveUserAuth(new AddUserAuthReq(jwtUser.getUid(),"wechat",openId));return getauthenticationToken(jwtUser,jwtUser.getAuthorities());}@Overridepublic boolean supports(Class<?> authentication) {return WechatAuthenticationToken.class.isAssignableFrom(authentication);}public WechatAuthenticationToken getauthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities){WechatAuthenticationToken authenticationToken=new WechatAuthenticationToken(principal,authorities);LinkedHashMap<Object, Object> linkedHashMap = new LinkedHashMap<>();linkedHashMap.put("principal", authenticationToken.getPrincipal());authenticationToken.setDetails(linkedHashMap);return authenticationToken;}
}

上述代码是一个自定义的认证提供者类,名为WeChatAuthenticationProvider。其主要功能是处理微信登录认证。在authenticate方法中,首先从传入的Authentication对象中提取出微信登录所需的参数,包括登录码、手机号码、昵称和头像URL。然后通过RestTemplate发送HTTP请求到微信API获取用户的openId,以验证用户身份。若成功获取openId,则检查系统中是否存在该用户的认证信息,若存在则直接返回认证token;若不存在,则继续获取用户的手机号,并根据手机号获取用户信息,并保存用户认证信息。最后,返回经过认证的token。

4.4. 编写WechatAuthenticationToken

public class WechatAuthenticationToken extends AbstractAuthenticationToken {private final Object principal;private  Object phoneCode;private Object nickName;private Object imageUrl;public WechatAuthenticationToken(String loginCode,String phoneCode,String nickName,String imageUrl) {super(null);this.principal = loginCode;this.phoneCode=phoneCode;this.nickName=nickName;this.imageUrl=imageUrl;setAuthenticated(false);}public WechatAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {super(authorities);this.principal = principal;super.setAuthenticated(true);}@Overridepublic Object getCredentials() {return null;}@Overridepublic Object getPrincipal() {return this.principal;}public Object getPhoneCode() {return phoneCode;}public Object getNickName() {return nickName;}public Object getImageUrl() {return imageUrl;}
}

4.5. WechatConfig

@Data
@Component
@ConfigurationProperties(prefix="wechat")
public class WechatConfig {private String appid;private String secret;
}

4.6. 更新WebSecurityConfigurer

@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {@Autowired@Qualifier("authUserDetailsServiceImpl")private UserDetailsService userDetailsService;@Autowiredprivate SecurOncePerRequestFilter securOncePerRequestFilter;@Autowiredprivate SecurAuthenticationEntryPoint securAuthenticationEntryPoint;@Autowiredprivate SecurAccessDeniedHandler securAccessDeniedHandler;//登录成功处理器@Autowiredprivate SecurAuthenticationSuccessHandler securAuthenticationSuccessHandler;@Autowiredprivate SecurAuthenticationFailureHandler securAuthenticationFailureHandler;//退出处理器@Autowiredprivate SecurLogoutHandler securLogoutHandler;@Autowiredprivate SecurLogoutSuccessHandler securLogoutSuccessHandler;@AutowiredBCryptPasswordEncoderUtil bCryptPasswordEncoderUtil;@Value("${wechat.appid}")private String appId;@Value("${wechat.secret}")private String secret;@AutowiredWechatConfig wechatConfig;@Autowiredprivate  WeChatService weChatService;@Autowiredprivate ISysUserAuthService sysUserAuthService;//    @Autowired
//    DynamicPermission dynamicPermission;/*** 从容器中取出 AuthenticationManagerBuilder,执行方法里面的逻辑之后,放回容器** @param authenticationManagerBuilder* @throws Exception*/@Autowiredpublic void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoderUtil);}@Overrideprotected void configure(HttpSecurity http) throws Exception {//第1步:解决跨域问题。cors 预检请求放行,让Spring security 放行所有preflight request(cors 预检请求)http.authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll();//第2步:让Security永远不会创建HttpSession,它不会使用HttpSession来获取SecurityContexthttp.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().headers().cacheControl();//第3步:请求权限配置//放行注册API请求,其它任何请求都必须经过身份验证.http.authorizeRequests()
//                .antMatchers("/**").permitAll().antMatchers(HttpMethod.POST,"/sys-user/register").permitAll().antMatchers(HttpMethod.GET,"/temp/create","/department/enable-department","/instance/**","/file/download/**").permitAll().antMatchers("/css/**", "/js/**", "/images/**", "/fonts/**","/editor-app/**","/model/**","/editor/**").permitAll().antMatchers("/modeler.html/**").permitAll().antMatchers("/feign/**").permitAll()//ROLE_ADMIN可以操作任何事情.antMatchers("/v2/api-docs", "/v2/feign-docs","/swagger-resources/configuration/ui","/swagger-resources","/swagger-resources/configuration/security","/swagger-ui.html", "/webjars/**").permitAll().antMatchers(HttpMethod.POST, "/user/wx/login").permitAll().anyRequest().authenticated();//                .antMatchers("/**").hasAnyAuthority("USER","SUPER_ADMIN","ADMIN");/*由于使用动态资源配置,以上代码在数据库中配置如下:在sys_backend_api_table中添加一条记录backend_api_id=1,backend_api_name = 所有API,backend_api_url=/**,backend_api_method=GET,POST,PUT,DELETE*///动态加载资源
//                .anyRequest().access("@dynamicPermission.checkPermisstion(request,authentication)");//第4步:拦截账号、密码。覆盖 UsernamePasswordAuthenticationFilter过滤器http.addFilterAt(securUsernamePasswordAuthenticationFilter() , UsernamePasswordAuthenticationFilter.class);//第5步:拦截token,并检测。在 UsernamePasswordAuthenticationFilter 之前添加 JwtAuthenticationTokenFilterhttp.addFilterBefore(securOncePerRequestFilter, UsernamePasswordAuthenticationFilter.class);http.addFilterBefore(weChatAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);//第6步:处理异常情况:认证失败和权限不足http.exceptionHandling().authenticationEntryPoint(securAuthenticationEntryPoint).accessDeniedHandler(securAccessDeniedHandler);//第7步:登录,因为使用前端发送JSON方式进行登录,所以登录模式不设置也是可以的。http.formLogin();//第8步:退出http.logout().addLogoutHandler(securLogoutHandler).logoutSuccessHandler(securLogoutSuccessHandler);}@Beanpublic WeChatAuthenticationFilter weChatAuthenticationFilter() throws Exception {WeChatAuthenticationFilter filter = new WeChatAuthenticationFilter(appId, secret);filter.setAuthenticationManager(authenticationManagerBean());filter.setAuthenticationSuccessHandler(securAuthenticationSuccessHandler);filter.setAuthenticationFailureHandler(securAuthenticationFailureHandler);return filter;}/*** 手动注册账号、密码拦截器* @return* @throws Exception*/@BeanSecurUsernamePasswordAuthenticationFilter securUsernamePasswordAuthenticationFilter() throws Exception {SecurUsernamePasswordAuthenticationFilter filter = new SecurUsernamePasswordAuthenticationFilter();//成功后处理filter.setAuthenticationSuccessHandler(securAuthenticationSuccessHandler);//失败后处理filter.setAuthenticationFailureHandler(securAuthenticationFailureHandler);filter.setAuthenticationManager(authenticationManagerBean());return filter;}@Beanpublic WeChatAuthenticationProvider weChatAuthenticationProvider() {return new WeChatAuthenticationProvider(wechatConfig,weChatService,sysUserAuthService);}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {// 添加微信登录认证提供者auth.authenticationProvider(weChatAuthenticationProvider());// 添加用户名密码登录认证提供者auth.authenticationProvider(daoAuthenticationProvider());}@Beanpublic DaoAuthenticationProvider daoAuthenticationProvider() {DaoAuthenticationProvider provider = new DaoAuthenticationProvider();provider.setUserDetailsService(userDetailsService);provider.setPasswordEncoder(new BCryptPasswordEncoder());return provider;}
}

说一个容易踩坑的地方:在Spring Security中,当你配置了自定义的认证提供者(如weChatAuthenticationProvider())来处理特定类型的认证(如微信登录),如果没有同时配置默认的认证提供者(如daoAuthenticationProvider()),则原有的基于用户名和密码的认证机制不会自动生效。这是因为Spring Security的认证机制是基于一个可配置的AuthenticationManager,它管理一个AuthenticationProvider列表,这些提供者会依次尝试认证用户提交的Authentication请求。

5. 结语

在本文中以流程讲解和代码实操讲解了如何在已有用户名和密码登录的基础上,实现微信小程序登录集成。下期将介绍基于OAuth2框架如何实现小程序登录,感兴趣的同学动动你们发财的小手点点关注吧~

 6. 参考链接 

开放能力 / 用户信息 / 手机号快速验证组件 (qq.com)

http://www.dinnco.com/news/29570.html

相关文章:

  • 网站建设关键技术线上营销推广方式有哪些
  • 移动网站制作公司友链交换网站源码
  • 怎么制作属于自己的网站山东seo网页优化外包
  • 做任务网站源码百度关键词搜索量统计
  • 公司建网站要多少钱网推接单平台
  • 高校网站建设的时效性关键词排名优化公司外包
  • apache php 多个网站发外链的论坛
  • 网站分页设计旺道网站排名优化
  • 建一个网络平台需要什么条件英语seo
  • 建材行业门户网站源码百度新闻
  • 邳州徐州网站开发厦门seo优化外包公司
  • 如何做电子海报在网站查关键词排名网
  • 电子政务与网站建设经验进入百度
  • 盐城网站建设多少钱找小网站的关键词
  • 在哪家网站可以买做服装的模具做网上营销怎样推广
  • php网站建设题目商业推广费用一般多少
  • 龙岗网站开发怎么在百度上做广告推广
  • 做网站买空间用共享ip曼联对利物浦新闻
  • 做网站须知域名免费注册0元注册
  • 医疗网站如何做优化黑马培训是正规学校吗
  • 网站建设教程视频百度云网络优化工程师为什么都说坑人
  • 网站做留言板怎么清空源码sem竞价专员
  • 长春火车站到龙嘉机场高铁时刻表网站seo搜索引擎的原理是什么
  • 个人网站名称举例搜索优化的培训免费咨询
  • 网站采用什么方法建设企业文化建设方案
  • 如何用dw做网站设计哈尔滨最新疫情通报
  • 专业做全景图的网站平台南宁网络推广有几家
  • 推荐几个好的网站link友情买卖
  • asp动态网站开发教程seo营销技巧
  • 汉寿做网站的公司外贸网站推广的方法