Java实现微信扫码登录并实现认证授权
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实现微信扫码登录并实现认证授权相关推荐
- 通过微信扫码登录剖析 oauth2 认证授权技术
本文目录 前言 趣味解读oauth2 oauth2精髓 oauth2核心概念 结合微信登录深刻理解oauht2 本文小结 前言 相信很多小伙伴在学习 JAVA 的过程中或多或少接触或者开发过类似于 x ...
- 微信扫码登录只能填一个授权回调域问题
背景 公司增加了个微信扫码登录,费劲千辛万苦终于把应用申请下来了,但遇到了一个头疼的事情:微信授权回调域只能写一个,且不支持通配.这下可好了,总不能把每个需要微信登录的二级域名都申请一个应用吧?而且一 ...
- 个人开发者实现微信扫码登录
使用码上登录中转微信扫码登录 使用之前最好有一个公网服务器,能够公网访问的 redis 和 mysql 数据库,并且能够部署公网访问的服务 码上登录是一个小程序,对个体开发者提供了免费的微信扫一扫登录 ...
- 后台管理使用微信扫码登录(小程序登录)
后台管理登录页添加一个微信登录按钮,点击弹窗二维码 代码实现: /*** 该页面原本是用户协议页(产线页面),目前接入微信授权登录功能* 原因:微信扫码登录,后端生成的二维码仅能跳转当前 生产环境小程 ...
- 微信扫码登录网站实现案例(无需授权)
前言 最近因业务需要,需要给网站增加一个扫描公众号二维码登录网站的功能,通过扫码登录,一来用户登录网站不需再输入账号密码,提升了用户体验,二来也可以作为公众号吸粉的一个渠道. 开发准备 实现微信扫码登 ...
- java实现简单扫码登录功能(模仿微信网页版扫码)
java实现简单扫码登录功能 模仿微信pc网页版扫码登录 使用js代码生成qrcode二维码减轻服务器压力 js循环请求服务端,判断是否qrcode被扫 二维码超时失效功能 二维码被扫成功登录,服务端 ...
- Java后台实现网站微信扫码登录功能,获取用户openid,及微信用户信息(小程序码方案),关联微信小程序(个人主体小程序也可以)
目录 前言 操作流程 1.注册微信小程序 2.通过后台获取小程序码 注意事项 时序图理解 方案实现步骤 前言 很多业务场景之下我们需要实现微信扫码登录检测登录状态的需求,或需要同步网站与小程序的用户信 ...
- Java+spring boot+mybatis plus+redis实现企业微信扫码登录并同步通讯录到数据库表中
1.首先需要有企业微信管理员账号,以便在企业微信管理端设置一些参数,参数如下: 企业id.AgentId-应用id.secret-应用的secret.授权登录的回调域名.通讯录同步下的secret. ...
- 项目整合微信扫码登录功能
项目整合微信登录功能 一.准备工作 https://open.weixin.qq.com 1.注册 2.邮箱激活 3.完善开发者资料 4.开发者资质认证 准备营业执照,1-2个工作日审批.300元 5 ...
最新文章
- maven 与intellij IDEA 下maven 为groovy 项目生成jar 详解
- Tensorflow tf.layers
- Codeforces Round #482 (Div. 2)
- CentOS挂载NTFS移动硬盘
- 猿辅导python面试_猿辅导面试经历—个人感受
- LeetCode每日一题:回文数(No.9)
- putty连接ubuntu虚拟机缓慢问题的解决
- PCL中的点云ICP配准(附源代码和数据)
- 行人仿真操作教程(一)行人仿真是什么?哪个软件好用?
- TeeChart Pro VCL,提供高性能图表
- 信息系统监理工程师模拟题
- MySQL (十四) 两阶段提交
- 详解Redo log与Undo log
- 用计算机玩游戏教程,Wegame怎么用手机玩电脑游戏 Wegame手机玩电脑游戏教程
- 正则表达式 re sys os random time 模块
- 私域经营中KOT、KOL、KOC 理解
- CSS属性 – cursor
- 中文,拼音分词使用练习记录
- 中小企业网上手机订货下单软件|移讯云订货通订单管理系统后台功能模块
- SoundPool播放.mp3音频
热门文章
- 三维模型还原记忆中的老房子!居然是她的毕业作品....
- Arcgis 只有边界线的情况下如何截取目标区域(掩膜裁剪)
- 如何做到精通GitLab CI/CD?
- 常州大学计算机专业老师,常州大学信息科学与工程学院导师教师师资介绍简介-潘操副高职称...
- cvte c++实习生面试题目
- Word 2019中公式不能自动斜体的解决方法
- 人工智能中的运筹学与最优化就该这样学之精品课程
- 北京医院排名 很有用,留下了
- 简单网站建设上线流程(小白专用)
- bat脚本中获取上级目录_批处理bat命令--获取当前盘符和当前目录和上级目录