Java实现微信扫码登录并实现认证授权

1.登录流程及原理

1.1 OAuth2协议

网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统。 在进行微信OAuth2.0授权登录接入之前,在微信开放平台注册开发者帐号,并拥有一个已审核通过的网站应用,并获得相应的 AppID 和 AppSecret,申请微信登录且通过审核后,可开始接入流程。

方案流程:

  +--------+                               +---------------+|        |--(A)- Authorization Request ->|   Resource    ||        |                               |     Owner     ||        |<-(B)-- Authorization Grant ---|               ||        |                               +---------------+|        ||        |                               +---------------+|        |--(C)-- Authorization Grant -->| Authorization || Client |                               |     Server    ||        |<-(D)----- Access Token -------|               ||        |                               +---------------+|        ||        |                               +---------------+|        |--(E)----- Access Token ------>|    Resource   ||        |                               |     Server    ||        |<-(F)--- Protected Resource ---|               |+--------+                               +---------------+

OAuth2包括以下角色:

1、客户端

本身不存储资源,需要通过资源拥有者的授权去请求资源服务器的资源,比如:手机客户端、浏览器等。

2、资源拥有者

通常为用户,也可以是应用程序,即该资源的拥有者。

A表示客户端请求资源拥有者授权。

B表示资源拥有者授权客户端访问自己的用户信息。

3、授权服务器(也称认证服务器)

认证服务器对资源拥有者进行认证,还会对客户端进行认证并颁发令牌。

C 客户端携带授权码请求认证。

D认证通过颁发令牌。

4、资源服务器

存储资源的服务器。

E表示客户端携带令牌请求资源服务器获取资源。

F表示资源服务器校验令牌通过后提供受保护资源。

2.2 微信扫码登录流程

以浏览器上扫码登录为例:

认证登录流程:

1、用户申请登录网站,扫微信二维码,请求微信授权登录;

2、用户确认后,微信端会携带code重定向到该网站;

3、网站带上code、appid、appsecret向微信端申请access_token;

4、微信返回access_token,网站带上access_token向微信服务端获取用户信息;

5、网站拿到信息后重定向到登陆界面即登陆成功。

2.代码实现

本项目认证服务需要做哪些事?

1、需要定义接口接收微信下发的授权码。

2、收到授权码调用微信接口申请令牌。

3、申请到令牌调用微信获取用户信息

4、获取用户信息成功将其写入本项目用户中心数据库。

5、最后重定向到浏览器自动登录。

代码如下:

2.1 controller

@Controller
public class WxLoginController {@AutowiredWxAuthServiceImpl wxAuthService;/*** 用户扫码确认登录后进入该接口,收到wx端重定向传过来的授权码,用授权码申请令牌,查询用户信息,写入用户信息* @param code 微信端返回的授权码* @param state 用于保持请求和回调的状态,授权请求后原样带回给第三方。*              该参数可用于防止 csrf 攻击(跨站请求伪造攻击),建议第三方带上该参数,*              可设置为简单的随机数加 session 进行校验* @return* @throws IOException*/@RequestMapping("/wxLogin")public String wxLogin(String code, String state) throws IOException {//拿授权码申请令牌,查询用户XcUser xcUser = wxAuthService.wxAuth(code);if(xcUser == null){//重定向到一个错误页面return "redirect:http://www.xxxxxxx.com/error.html";}else{String username = xcUser.getUsername();//重定向到登录页面,自动登录return "redirect:http://www.xxxxxxx.com/sign.html?username="+username+"&authType=wx";}}
}

2.2 WxAuthServiceImpl

这里直接用service实现类;

@Service("wx_authservice")
public class WxAuthServiceImpl implements AuthService {@AutowiredUserMapper userMapper;@Value("${weixin.appid}")String appid;@Value("${weixin.secret}")String secret;@AutowiredRestTemplate restTemplate;@AutowiredUserRoleMapper userRoleMapper;@AutowiredWxAuthServiceImpl currentProxy;//拿授权码申请令牌,查询用户public User wxAuth(String code) {//拿授权码获取access_tokenMap<String, String> access_token_map = getAccess_token(code);System.out.println(access_token_map);//得到令牌String access_token = access_token_map.get("access_token");//得到openidString openid = access_token_map.get("openid");//拿令牌获取用户信息Map<String, String> userinfo = getUserinfo(access_token, openid);System.out.println(userinfo);//添加用户到数据库User User = currentProxy.addWxUser(userinfo);return User;}@Transactionalpublic User addWxUser(Map userInfo_map){//先取出unionidString unionid = (String) userInfo_map.get("unionid");//根据unionid查询数据库User User = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getWxUnionid, unionid));if(User!=null){//该用户在系统存在return User;}User = new User();//用户idString id = UUID.randomUUID().toString();User.setId(id);User.setWxUnionid(unionid);//记录从微信得到的昵称User.setNickname(userInfo_map.get("nickname").toString());User.setUserpic(userInfo_map.get("headimgurl").toString());User.setName(userInfo_map.get("nickname").toString());User.setUsername(unionid);User.setPassword(unionid);User.setUtype("101001");//学生类型User.setStatus("1");//用户状态User.setCreateTime(LocalDateTime.now());userMapper.insert(User);UserRole UserRole = new UserRole();UserRole.setId(UUID.randomUUID().toString());UserRole.setUserId(id);UserRole.setRoleId("17");//学生角色userRoleMapper.insert(UserRole);return User;}//请求微信获取令牌/*** 微信接口响应结果* {* "access_token":"ACCESS_TOKEN",* "expires_in":7200,* "refresh_token":"REFRESH_TOKEN",* "openid":"OPENID",* "scope":"SCOPE",* "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"* }*/private Map<String, String> getAccess_token(String code) {String url_template = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";String url = String.format(url_template, appid, secret, code);//请求微信获取令牌ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, null, String.class);System.out.println(response);//得到响应串String responseString = response.getBody();//将json串转成mapMap map = JSON.parseObject(responseString, Map.class);return map;}//携带令牌查询用户信息//http请求方式: GET//https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID/**{"openid":"OPENID","nickname":"NICKNAME","sex":1,"province":"PROVINCE","city":"CITY","country":"COUNTRY","headimgurl": "https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJfHe/0","privilege":["PRIVILEGE1","PRIVILEGE2"],"unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"}*/private Map<String,String> getUserinfo(String access_token,String openid) {//请求微信查询用户信息String url_template = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s";String url = String.format(url_template,access_token,openid);ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, null, String.class);String body = response.getBody();//将结果转成mapMap map = JSON.parseObject(body, Map.class);return map;}
}

3.认证授权

项目集成了Spring Security,还需从用户信息中获取该用户的权限信息;

3.1 UserServiceImpl

重写了Spring Security的用户认证方式,使其接入微信登录认证;

authParamsDto 认证参数定义;

/*** @description 统一认证入口后统一提交的数据*/
@Data
public class AuthParamsDto {private String username; //用户名private String password; //域  用于扩展private String cellphone;//手机号private String checkcode;//验证码private String checkcodekey;//验证码keyprivate String authType; // 认证的类型   password:用户名密码模式类型    sms:短信模式类型private Map<String, Object> payload = new HashMap<>();//附加数据,作为扩展,不同认证类型可拥有不同的附加数据。如认证类型为短信时包含smsKey : sms:3d21042d054548b08477142bbca95cfa; 所有情况下都包含clientId
}

用户扩展信息定义;

/*** @description 用户扩展信息*/
@Data
public class XcUserExt extends XcUser {//用户权限List<String> permissions = new ArrayList<>();
}

loadUserByUsername()方法重写,使其支持微信认证;

@Slf4j
@Service
public class UserServiceImpl implements UserDetailsService {@AutowiredUserMapper userMapper;@AutowiredApplicationContext applicationContext;@AutowiredMenuMapper menuMapper;//菜单权限mapper//传入的是AuthParamsDto的json串@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {AuthParamsDto authParamsDto = null;try {//将认证参数转为AuthParamsDto类型authParamsDto = JSON.parseObject(s, AuthParamsDto.class);} catch (Exception e) {log.info("认证请求不符合项目要求:{}",s);throw new RuntimeException("认证请求数据格式不对");}//认证方式,String authType = authParamsDto.getAuthType();//从spring容器中拿具体的认证bean实例AuthService authService = applicationContext.getBean(authType + "_authservice", AuthService.class);//开始认证,认证成功拿到用户信息UserExt UserExt = authService.execute(authParamsDto);return getUserPrincipal(UserExt);}//根据UserExt对象构造一个UserDetails对象/*** @description 查询用户信息* @param user  用户id,主键* @return 用户信息*/public UserDetails getUserPrincipal(UserExt user){//权限列表,存放的用户权限List<String> permissionList = new ArrayList<>();//根据用户id查询数据库中他的权限List<Menu> Menus = menuMapper.selectPermissionByUserId(user.getId());Menus.forEach(menu->{permissionList.add(menu.getCode());});if(permissionList.size()==0){//用户权限,如果不加报Cannot pass a null GrantedAuthority collectionpermissionList.add("test");}String[] authorities= permissionList.toArray(new String[0]);//原来存的是账号,现在扩展为用户的全部信息(密码不要放)user.setPassword(null);String jsonString = JSON.toJSONString(user);UserDetails userDetails = User.withUsername(jsonString).password("").authorities(authorities).build();return userDetails;}}

3.2 service接口

public interface AuthService {/*** @description 认证方法* @param authParamsDto 认证参数* @return 用户信息*/UserExt execute(AuthParamsDto authParamsDto);}

上述execute()方法在微信登录服务实现类WxAuthServiceImpl中实现

//微信认证方法@Overridepublic UserExt execute(AuthParamsDto authParamsDto) {//获取账号String username = authParamsDto.getUsername();User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername,username));if(user==null){throw new RuntimeException("用户不存在");}UserExt userExt = new UserExt();BeanUtils.copyProperties(user, userExt);return userExt;}

3.3 自定义DaoAuthenticationProvider;

SpringSecurity框架默认是密码校验模式,将其重写为空,使其不再校验密码;

@Slf4j
@Component
public class DaoAuthenticationProviderCustom extends DaoAuthenticationProvider {@Autowired@Overridepublic void setUserDetailsService(UserDetailsService userDetailsService) {super.setUserDetailsService(userDetailsService);}@Override//不再校验密码protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {}

修改WebSecurityConfig类指定自定义的daoAuthenticationProviderCustom

@AutowiredDaoAuthenticationProviderCustom daoAuthenticationProviderCustom;//使用自己定义DaoAuthenticationProviderCustom来代替框架的DaoAuthenticationProvider@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.authenticationProvider(daoAuthenticationProviderCustom);}

至此我们基于Spring Security认证流程修改为如下:

完成!

Java实现微信扫码登录并实现认证授权相关推荐

  1. 通过微信扫码登录剖析 oauth2 认证授权技术

    本文目录 前言 趣味解读oauth2 oauth2精髓 oauth2核心概念 结合微信登录深刻理解oauht2 本文小结 前言 相信很多小伙伴在学习 JAVA 的过程中或多或少接触或者开发过类似于 x ...

  2. 微信扫码登录只能填一个授权回调域问题

    背景 公司增加了个微信扫码登录,费劲千辛万苦终于把应用申请下来了,但遇到了一个头疼的事情:微信授权回调域只能写一个,且不支持通配.这下可好了,总不能把每个需要微信登录的二级域名都申请一个应用吧?而且一 ...

  3. 个人开发者实现微信扫码登录

    使用码上登录中转微信扫码登录 使用之前最好有一个公网服务器,能够公网访问的 redis 和 mysql 数据库,并且能够部署公网访问的服务 码上登录是一个小程序,对个体开发者提供了免费的微信扫一扫登录 ...

  4. 后台管理使用微信扫码登录(小程序登录)

    后台管理登录页添加一个微信登录按钮,点击弹窗二维码 代码实现: /*** 该页面原本是用户协议页(产线页面),目前接入微信授权登录功能* 原因:微信扫码登录,后端生成的二维码仅能跳转当前 生产环境小程 ...

  5. 微信扫码登录网站实现案例(无需授权)

    前言 最近因业务需要,需要给网站增加一个扫描公众号二维码登录网站的功能,通过扫码登录,一来用户登录网站不需再输入账号密码,提升了用户体验,二来也可以作为公众号吸粉的一个渠道. 开发准备 实现微信扫码登 ...

  6. java实现简单扫码登录功能(模仿微信网页版扫码)

    java实现简单扫码登录功能 模仿微信pc网页版扫码登录 使用js代码生成qrcode二维码减轻服务器压力 js循环请求服务端,判断是否qrcode被扫 二维码超时失效功能 二维码被扫成功登录,服务端 ...

  7. Java后台实现网站微信扫码登录功能,获取用户openid,及微信用户信息(小程序码方案),关联微信小程序(个人主体小程序也可以)

    目录 前言 操作流程 1.注册微信小程序 2.通过后台获取小程序码 注意事项 时序图理解 方案实现步骤 前言 很多业务场景之下我们需要实现微信扫码登录检测登录状态的需求,或需要同步网站与小程序的用户信 ...

  8. Java+spring boot+mybatis plus+redis实现企业微信扫码登录并同步通讯录到数据库表中

    1.首先需要有企业微信管理员账号,以便在企业微信管理端设置一些参数,参数如下: 企业id.AgentId-应用id.secret-应用的secret.授权登录的回调域名.通讯录同步下的secret. ...

  9. 项目整合微信扫码登录功能

    项目整合微信登录功能 一.准备工作 https://open.weixin.qq.com 1.注册 2.邮箱激活 3.完善开发者资料 4.开发者资质认证 准备营业执照,1-2个工作日审批.300元 5 ...

最新文章

  1. maven 与intellij IDEA 下maven 为groovy 项目生成jar 详解
  2. Tensorflow tf.layers
  3. Codeforces Round #482 (Div. 2)
  4. CentOS挂载NTFS移动硬盘
  5. 猿辅导python面试_猿辅导面试经历—个人感受
  6. LeetCode每日一题:回文数(No.9)
  7. putty连接ubuntu虚拟机缓慢问题的解决
  8. PCL中的点云ICP配准(附源代码和数据)
  9. 行人仿真操作教程(一)行人仿真是什么?哪个软件好用?
  10. TeeChart Pro VCL,提供高性能图表
  11. 信息系统监理工程师模拟题
  12. MySQL (十四) 两阶段提交
  13. 详解Redo log与Undo log
  14. 用计算机玩游戏教程,Wegame怎么用手机玩电脑游戏 Wegame手机玩电脑游戏教程
  15. 正则表达式 re sys os random time 模块
  16. 私域经营中KOT、KOL、KOC 理解
  17. CSS属性 – cursor
  18. 中文,拼音分词使用练习记录
  19. 中小企业网上手机订货下单软件|移讯云订货通订单管理系统后台功能模块
  20. SoundPool播放.mp3音频

热门文章

  1. 三维模型还原记忆中的老房子!居然是她的毕业作品....
  2. Arcgis 只有边界线的情况下如何截取目标区域(掩膜裁剪)
  3. 如何做到精通GitLab CI/CD?
  4. 常州大学计算机专业老师,常州大学信息科学与工程学院导师教师师资介绍简介-潘操副高职称...
  5. cvte c++实习生面试题目
  6. Word 2019中公式不能自动斜体的解决方法
  7. 人工智能中的运筹学与最优化就该这样学之精品课程
  8. 北京医院排名 很有用,留下了
  9. 简单网站建设上线流程(小白专用)
  10. bat脚本中获取上级目录_批处理bat命令--获取当前盘符和当前目录和上级目录