/* * Copyright (c) 2018-2028, Chill Zhuang All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * Neither the name of the dreamlu.net developer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * Author: Chill 庄骞 (smallchill@163.com) */ package cn.gistack.auth.endpoint; import cn.gistack.auth.props.LoginUser; import cn.gistack.auth.props.OAuthProperties; import cn.gistack.common.utils.HttpClientUtils; import cn.gistack.system.user.entity.User; import cn.gistack.system.user.feign.IUserClient; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.wf.captcha.SpecCaptcha; import lombok.AllArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import cn.gistack.common.cache.CacheNames; import org.springblade.core.cache.utils.CacheUtil; import org.springblade.core.jwt.JwtUtil; import org.springblade.core.jwt.props.JwtProperties; import org.springblade.core.launch.constant.TokenConstant; import org.springblade.core.redis.cache.BladeRedis; import org.springblade.core.secure.BladeUser; import org.springblade.core.secure.utils.AuthUtil; import org.springblade.core.tenant.annotation.NonDS; import org.springblade.core.tool.api.R; import org.springblade.core.tool.support.Kv; import org.springblade.core.tool.utils.StringUtil; import org.springblade.core.tool.utils.WebUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2RefreshToken; import org.springframework.security.oauth2.provider.AuthorizationRequest; import org.springframework.security.oauth2.provider.ClientDetailsService; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.time.Duration; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import static cn.gistack.common.cache.CacheNames.OAUTH_KEY; import static org.springblade.core.cache.constant.CacheConstant.*; /** * BladeEndPoint * * @author Chill */ @NonDS @Slf4j @RestController @AllArgsConstructor public class BladeTokenEndPoint { private final BladeRedis bladeRedis; private final JwtProperties jwtProperties; private final ClientDetailsService clientDetailsService; private final TokenStore tokenStore; private final IUserClient userClient; private final UserDetailsService userDetailsService; //获取wxAccessToken private final String WX_GET_ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token"; private final String WX_GET_PHONE_NUMBER = "https://api.weixin.qq.com/wxa/business/getuserphonenumber"; private final String WX_APP_ID = "wx521ae71bcde281bc"; private final String WX_SECRET = "c1c93b0c4545f8cdf42752121ea8458c"; @Autowired private final OAuthProperties oauthProperties; /** * 登录页面 */ @GetMapping("/oauth/login") public ModelAndView require(ModelAndView model) { model.setViewName("login"); return model; } @SneakyThrows @GetMapping("/oauth/tokenLoginUI") public void tokenLoginUI(HttpServletResponse response, String code) { if (StringUtil.isBlank(code)) { response.sendRedirect(oauthProperties.getRedirectExitUri()); } else { response.sendRedirect(oauthProperties.getRedirectWebUri() + "?code=" + code); } } @GetMapping("/oauth/tokenLogin") public R tokenLogin(String code) { // 统一登录获取accessToken String accessToken = getAccessToken(code); System.out.println("accessToken:" + accessToken); LoginUser loginUser = getUsernameByToken(accessToken); loginUser.setCode(code); bladeRedis.setEx(OAUTH_KEY + code,code,5*60L); return R.data(loginUser); } private String getAccessToken(String code) { // 统一登录获取accessToken String url = oauthProperties.getTokenUrl(); Map param = new HashMap<>(); param.put("grant_type", "authorization_code"); param.put("redirect_uri", oauthProperties.getRedirectUri()); param.put("client_id", oauthProperties.getClientId()); param.put("client_secret", oauthProperties.getClientSecret()); param.put("AppKey", oauthProperties.getAppKey()); param.put("AppSecret", oauthProperties.getAppSecret()); param.put("code", code); String accessToken = HttpClientUtils.doGet(url, param); Map maps = (Map) JSON.parse(accessToken); if (maps.containsKey("access_token")) { return maps.get("access_token"); } return ""; } private LoginUser getUsernameByToken(String accessToken) { String url = oauthProperties.getProfileUrl(); Map param = new HashMap<>(); param.put("AppKey", oauthProperties.getAppKey()); param.put("AppSecret", oauthProperties.getAppSecret()); param.put("accessToken", accessToken); String resp = HttpClientUtils.doGet(url, param); System.out.println("接收数据:" + resp); LoginUser loginUser = JSON.parseObject(resp, LoginUser.class); return loginUser; } @GetMapping("/oauth/wxLogin") public R wxLogin(String code) { String wxAccessToken = getWxAccessToken(); //参数 String url = WX_GET_PHONE_NUMBER + "?access_token=" + wxAccessToken; //向微信接口发起请求获取手机号 RestTemplate template = new RestTemplate(); Map params = new LinkedHashMap<>(); params.put("code", code); String result = template.postForObject(url, params, String.class); JSONObject resultObj = (JSONObject) JSONObject.parse(result); JSONObject phoneInfo = (JSONObject) resultObj.get("phone_info"); String phoneNumber = phoneInfo.get("phoneNumber").toString(); R userR = userClient.userByAccount("000000", phoneNumber); if (userR.getData() != null) { return R.data(phoneNumber); } else { return R.fail("不存在该用户"); } } public String getWxAccessToken() { //参数 Map params = new HashMap<>(); params.put("grant_type", "client_credential"); params.put("appid", WX_APP_ID); params.put("secret", WX_SECRET); String result = HttpClientUtils.doGet(WX_GET_ACCESS_TOKEN, params); JSONObject jsonObject = JSON.parseObject(result); String accessToken = jsonObject.getString("access_token"); return accessToken; } /** * 授权页面 */ @GetMapping("/oauth/confirm_access") public ModelAndView confirm(HttpSession session, ModelAndView model) { Object auth = session.getAttribute("authorizationRequest"); if (auth != null) { AuthorizationRequest authorizationRequest = (AuthorizationRequest) auth; model.addObject("client", clientDetailsService.loadClientByClientId(authorizationRequest.getClientId())); model.addObject("principal", SecurityContextHolder.getContext().getAuthentication().getPrincipal()); } model.setViewName("confirm"); return model; } /** * 用户信息 */ @GetMapping("/oauth/user-info") public R currentUser(Authentication authentication) { return R.data(authentication); } /** * 验证码 */ @GetMapping("/oauth/captcha") public Kv captcha() { SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5); String verCode = specCaptcha.text().toLowerCase(); String key = StringUtil.randomUUID(); // 存入redis并设置过期时间为30分钟 bladeRedis.setEx(CacheNames.CAPTCHA_KEY + key, verCode, Duration.ofMinutes(30)); // 将key和base64返回给前端 return Kv.create().set("key", key).set("image", specCaptcha.toBase64()); } /** * 退出登录 */ @GetMapping("/oauth/logout") public Kv logout() { BladeUser user = AuthUtil.getUser(); String token = JwtUtil.getToken(WebUtil.getRequest().getHeader(TokenConstant.HEADER)); // 清空redis保存的token if (user != null && jwtProperties.getState()) { JwtUtil.removeAccessToken(user.getTenantId(), String.valueOf(user.getUserId()), token); } // 清空资源服务器保存的token OAuth2AccessToken accessToken = tokenStore.readAccessToken(token); OAuth2RefreshToken refreshToken = null; if (accessToken != null && StringUtil.isNoneBlank(accessToken.getValue())) { refreshToken = accessToken.getRefreshToken(); tokenStore.removeAccessToken(accessToken); } if (refreshToken != null && StringUtil.isNoneBlank(refreshToken.getValue())) { tokenStore.removeRefreshToken(refreshToken); } return Kv.create().set("success", "true").set("msg", "success"); } /** * 缓存清空 */ @GetMapping("/oauth/clear-cache") public Kv clearCache() { CacheUtil.clear(BIZ_CACHE); CacheUtil.clear(USER_CACHE); CacheUtil.clear(DICT_CACHE); CacheUtil.clear(FLOW_CACHE); CacheUtil.clear(SYS_CACHE); CacheUtil.clear(PARAM_CACHE); CacheUtil.clear(RESOURCE_CACHE); CacheUtil.clear(MENU_CACHE); CacheUtil.clear(DICT_CACHE, Boolean.FALSE); CacheUtil.clear(MENU_CACHE, Boolean.FALSE); CacheUtil.clear(SYS_CACHE, Boolean.FALSE); CacheUtil.clear(PARAM_CACHE, Boolean.FALSE); return Kv.create().set("success", "true").set("msg", "success"); } }