|
@ -1,18 +1,19 @@
|
|
|
package com.yihu.jw.security.oauth2.provider.endpoint;
|
|
|
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
import com.yihu.jw.security.model.Captcha;
|
|
|
import com.yihu.jw.security.model.PublicKey;
|
|
|
import com.yihu.jw.security.oauth2.core.redis.WlyyRedisVerifyCodeService;
|
|
|
import com.yihu.jw.security.oauth2.provider.error.WlyyOAuth2ExceptionTranslator;
|
|
|
import com.yihu.jw.security.oauth2.provider.WlyyTokenGranter;
|
|
|
import com.yihu.jw.security.core.userdetails.jdbc.WlyyUserDetailsService;
|
|
|
import com.yihu.jw.security.model.Oauth2Envelop;
|
|
|
import com.yihu.jw.security.model.WlyyUserSimple;
|
|
|
import com.yihu.utils.security.RSAUtils;
|
|
|
import org.apache.commons.codec.binary.Base64;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.http.HttpHeaders;
|
|
|
import org.springframework.http.HttpStatus;
|
|
|
import org.springframework.http.ResponseEntity;
|
|
|
import org.springframework.http.*;
|
|
|
import org.springframework.security.authentication.AuthenticationManager;
|
|
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
|
|
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
|
@ -24,15 +25,27 @@ import org.springframework.security.oauth2.provider.request.DefaultOAuth2Request
|
|
|
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
|
|
|
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
|
|
|
import org.springframework.util.Assert;
|
|
|
import org.springframework.util.LinkedMultiValueMap;
|
|
|
import org.springframework.util.MultiValueMap;
|
|
|
import org.springframework.util.StringUtils;
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
import org.springframework.web.client.RestTemplate;
|
|
|
import org.springframework.web.context.request.RequestContextHolder;
|
|
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
|
|
|
|
|
import javax.annotation.PostConstruct;
|
|
|
import javax.servlet.http.Cookie;
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
import javax.servlet.http.HttpSession;
|
|
|
import java.io.IOException;
|
|
|
import java.security.interfaces.RSAPrivateKey;
|
|
|
import java.security.interfaces.RSAPublicKey;
|
|
|
import java.text.SimpleDateFormat;
|
|
|
import java.util.Date;
|
|
|
import java.util.HashMap;
|
|
|
import java.util.Map;
|
|
|
import java.util.UUID;
|
|
|
|
|
|
/**
|
|
|
* <p>
|
|
@ -64,26 +77,36 @@ public class WlyyLoginEndpoint extends AbstractEndpoint {
|
|
|
@Autowired
|
|
|
private WlyyUserDetailsService userDetailsService;
|
|
|
@Autowired
|
|
|
private WlyyRedisVerifyCodeService wlyyRedisVerifyCodeService;
|
|
|
private RestTemplate restTemplate;
|
|
|
@Autowired
|
|
|
private ObjectMapper objectMapper;
|
|
|
|
|
|
private WlyyRedisVerifyCodeService wlyyRedisVerifyCodeService;
|
|
|
|
|
|
@PostConstruct
|
|
|
private void init() {
|
|
|
super.setTokenGranter(tokenGranter);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 登陆
|
|
|
* @param parameters
|
|
|
* @param httpSession
|
|
|
* @return
|
|
|
* @throws Exception
|
|
|
*/
|
|
|
@RequestMapping(value = "/oauth/login", method = RequestMethod.POST)
|
|
|
public ResponseEntity<Oauth2Envelop<WlyyUserSimple>> login(@RequestParam Map<String, String> parameters) {
|
|
|
public ResponseEntity<Oauth2Envelop<WlyyUserSimple>> login(@RequestParam Map<String, String> parameters, HttpSession httpSession) throws Exception {
|
|
|
String client_id = parameters.get("client_id");
|
|
|
if (StringUtils.isEmpty(client_id)) {
|
|
|
throw new InvalidRequestException("client_id");
|
|
|
}
|
|
|
if (StringUtils.isEmpty(parameters.get("verify_code"))) {
|
|
|
if (StringUtils.isEmpty(parameters.get("captcha"))) {
|
|
|
parameters.put("grant_type", "password");
|
|
|
if (parameters.get("password") != null) {
|
|
|
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey)httpSession.getAttribute("privateKey");
|
|
|
parameters.put("password", RSAUtils.decryptByPrivateKey(new String(Base64.decodeBase64(parameters.get("password"))), rsaPrivateKey));
|
|
|
}
|
|
|
} else {
|
|
|
parameters.put("grant_type", "verify_code");
|
|
|
parameters.put("grant_type", "captcha");
|
|
|
}
|
|
|
|
|
|
ClientDetails authenticatedClient = clientDetailsService.loadClientByClientId(client_id);
|
|
@ -118,6 +141,11 @@ public class WlyyLoginEndpoint extends AbstractEndpoint {
|
|
|
return getResponse(wlyyUserSimple);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 单点登陆第二步 - token验证
|
|
|
* @param parameters
|
|
|
* @return
|
|
|
*/
|
|
|
@RequestMapping(value = "/oauth/sso", method = RequestMethod.POST)
|
|
|
public ResponseEntity<Oauth2Envelop<WlyyUserSimple>> sso(@RequestParam Map<String, String> parameters) {
|
|
|
String clientId = parameters.get("client_id");
|
|
@ -154,6 +182,12 @@ public class WlyyLoginEndpoint extends AbstractEndpoint {
|
|
|
return getResponse(wlyyUserSimple);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 登出
|
|
|
* @param parameters
|
|
|
* @param request
|
|
|
* @return
|
|
|
*/
|
|
|
@RequestMapping(value = "/oauth/logout", method = RequestMethod.POST)
|
|
|
public ResponseEntity<Oauth2Envelop> logout(@RequestParam Map<String, String> parameters, HttpServletRequest request) {
|
|
|
String token = request.getHeader("token");
|
|
@ -172,112 +206,125 @@ public class WlyyLoginEndpoint extends AbstractEndpoint {
|
|
|
return new ResponseEntity<>(oauth2Envelop, headers, HttpStatus.OK);
|
|
|
}
|
|
|
|
|
|
/*@RequestMapping(value = ServiceApi.Authentication.VerifyCode, method = RequestMethod.POST)
|
|
|
public ResponseEntity<Envelop> verifyCode(@RequestParam Map<String, String> parameters) throws Exception{
|
|
|
Envelop envelop = new Envelop();
|
|
|
/**
|
|
|
* 获取公钥
|
|
|
* @param httpSession
|
|
|
* @param httpServletResponse
|
|
|
* @return
|
|
|
* @throws Exception
|
|
|
*/
|
|
|
@RequestMapping(value = "/oauth/public_key", method = RequestMethod.GET)
|
|
|
public ResponseEntity<Oauth2Envelop<PublicKey>> publicKey (
|
|
|
HttpSession httpSession,
|
|
|
HttpServletResponse httpServletResponse) throws Exception {
|
|
|
//生成公钥和私钥
|
|
|
HashMap<String, Object> map = RSAUtils.generateKeys();
|
|
|
RSAPublicKey rsaPublicKey = (RSAPublicKey) map.get("public");
|
|
|
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) map.get("private");
|
|
|
PublicKey publicKey = new PublicKey();
|
|
|
publicKey.setModulus(Base64.encodeBase64String(rsaPublicKey.getModulus().toByteArray()));
|
|
|
publicKey.setExponent(Base64.encodeBase64String(rsaPublicKey.getPublicExponent().toByteArray()));
|
|
|
httpSession.setAttribute("privateKey", rsaPrivateKey);
|
|
|
//生成Cookie
|
|
|
Cookie cookie = new Cookie("oauth2", UUID.randomUUID().toString());
|
|
|
cookie.setMaxAge(60);
|
|
|
cookie.setPath("/oauth");
|
|
|
httpServletResponse.addCookie(cookie);
|
|
|
HttpHeaders headers = new HttpHeaders();
|
|
|
headers.set("Cache-Control", "no-store");
|
|
|
headers.set("Pragma", "no-cache");
|
|
|
Oauth2Envelop<PublicKey> oauth2Envelop = new Oauth2Envelop<>("public_key", 200, publicKey);
|
|
|
return new ResponseEntity<>(oauth2Envelop, headers, HttpStatus.OK);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取验证码
|
|
|
* @param parameters
|
|
|
* @return
|
|
|
* @throws Exception
|
|
|
*/
|
|
|
@RequestMapping(value = "/oauth/captcha", method = RequestMethod.GET)
|
|
|
public ResponseEntity<Oauth2Envelop<Captcha>> captcha(@RequestParam Map<String, String> parameters) throws Exception{
|
|
|
String client_id = parameters.get("client_id");
|
|
|
String username = parameters.get("username");
|
|
|
if (StringUtils.isEmpty(client_id)) {
|
|
|
throw new InvalidRequestException("client_id");
|
|
|
}
|
|
|
if (StringUtils.isEmpty(username)){
|
|
|
envelop.setSuccessFlg(false);
|
|
|
envelop.setErrorMsg("手机号码【"+username+"】不能为空!");
|
|
|
return new ResponseEntity<>(envelop, headers, HttpStatus.OK);
|
|
|
throw new InvalidRequestException("username");
|
|
|
}
|
|
|
VerifyCode verifyCode = new VerifyCode();
|
|
|
//手机短信验证码
|
|
|
RandomUtil randomUtil = new RandomUtil();
|
|
|
String random = randomUtil.getRandomString(6);
|
|
|
//发送短信
|
|
|
String api = "MsgGW.Sms.send";
|
|
|
String content = "尊敬的用户:欢迎使用健康上饶,您的验证码为:【" + random + "】,有效期10分钟,请尽快完成注册。若非本人操作,请忽略。";
|
|
|
Map<String, String> apiParamMap = new HashMap<>();
|
|
|
//手机号码
|
|
|
apiParamMap.put("mobile", username);
|
|
|
//业务标签
|
|
|
apiParamMap.put("handlerId", fzHandlerId);
|
|
|
//短信内容
|
|
|
apiParamMap.put("content", content);
|
|
|
//渠道号
|
|
|
apiParamMap.put("clientId", fzClientId);
|
|
|
String result = null;
|
|
|
Envelop resultEnvelop = fzApiClient.fzInnerApi(api, objectMapper.writeValueAsString(apiParamMap), 1);
|
|
|
if (resultEnvelop.isSuccessFlg()) {
|
|
|
result = resultEnvelop.getObj().toString();
|
|
|
//验证请求间隔超时,防止频繁获取验证码
|
|
|
if (!wlyyRedisVerifyCodeService.isIntervalTimeout(client_id, username)) {
|
|
|
throw new IllegalAccessException("SMS request frequency is too fast");
|
|
|
}
|
|
|
if (!StringUtils.isEmpty(result)) {
|
|
|
Map<String, Object> resultMap = objectMapper.readValue(result, Map.class);
|
|
|
Integer resultCode = 0;
|
|
|
if (null != resultMap.get("Code") && !"".equals(resultMap.get("Code"))) {
|
|
|
resultCode = Integer.valueOf(resultMap.get("Code").toString());
|
|
|
}
|
|
|
if (resultCode == 10000) {
|
|
|
verifyCode.setExpiresIn(600);
|
|
|
verifyCode.setNextRequestTime(60);
|
|
|
//验证码有效期
|
|
|
ehrRedisVerifyCodeService.store(client_id, username, random, 600000);
|
|
|
envelop.setSuccessFlg(true);
|
|
|
envelop.setObj(verifyCode);
|
|
|
} else if(resultCode == -201){
|
|
|
envelop.setSuccessFlg(false);
|
|
|
envelop.setErrorCode(resultCode);
|
|
|
envelop.setErrorMsg("短信已达每天限制的次数(10次)!");
|
|
|
} else if(resultCode == -200){
|
|
|
envelop.setSuccessFlg(false);
|
|
|
envelop.setErrorCode(resultCode);
|
|
|
envelop.setErrorMsg("短信发送频率太快(不能低于60s)!");
|
|
|
} else {
|
|
|
envelop.setSuccessFlg(false);
|
|
|
envelop.setErrorCode(resultCode);
|
|
|
envelop.setErrorMsg("短信验证码发送失败!");
|
|
|
}
|
|
|
} else {
|
|
|
envelop.setSuccessFlg(false);
|
|
|
envelop.setErrorCode(ErrorCode.REQUEST_NOT_COMPLETED.value());
|
|
|
envelop.setErrorMsg("短信验证码发送失败!");
|
|
|
//发送短信获取验证码
|
|
|
HttpHeaders reqHeaders = new HttpHeaders();
|
|
|
reqHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
|
|
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
|
|
|
params.add("clientId", client_id);
|
|
|
params.add("type", "login");
|
|
|
params.add("to", username);
|
|
|
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(params, reqHeaders);
|
|
|
HashMap<String, Object> result = restTemplate.postForObject("http://svr-base:10020/sms_gateway/send", httpEntity, HashMap.class);
|
|
|
if (200 == (Integer) result.get("status")){
|
|
|
Map<String, Object> sms = (Map)result.get("obj");
|
|
|
String captcha = (String) sms.get("captcha");
|
|
|
Date deadline = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse((String) sms.get("deadline"));
|
|
|
Long expire = (deadline.getTime() - new Date().getTime()) / 1000;
|
|
|
Captcha _captcha = new Captcha();
|
|
|
_captcha.setCode(captcha);
|
|
|
_captcha.setExpiresIn(expire.intValue());
|
|
|
wlyyRedisVerifyCodeService.store(client_id, username, captcha, expire.intValue());
|
|
|
Oauth2Envelop<Captcha> oauth2Envelop = new Oauth2Envelop<>("captcha", 200, _captcha);
|
|
|
HttpHeaders headers = new HttpHeaders();
|
|
|
headers.set("Cache-Control", "no-store");
|
|
|
headers.set("Pragma", "no-cache");
|
|
|
return new ResponseEntity<>(oauth2Envelop, headers, HttpStatus.OK);
|
|
|
}
|
|
|
return new ResponseEntity<>(envelop, headers, HttpStatus.OK);
|
|
|
}
|
|
|
|
|
|
@RequestMapping(value = ServiceApi.Authentication.VerifyCodeExpire, method = RequestMethod.POST)
|
|
|
public ResponseEntity<VerifyCode> verifyCodeExpire(@RequestParam Map<String, String> parameters) {
|
|
|
String client_id = parameters.get("client_id");
|
|
|
String username = parameters.get("username");
|
|
|
VerifyCode verifyCode = new VerifyCode();
|
|
|
int expiresIn = ehrRedisVerifyCodeService.getExpireTime(client_id, username);
|
|
|
int nextRequestTime = 60 + (expiresIn - 600 ) > 0 ? 60 + (expiresIn - 600 ) : 0;
|
|
|
verifyCode.setNextRequestTime(nextRequestTime);
|
|
|
verifyCode.setExpiresIn(expiresIn);
|
|
|
throw new IllegalStateException((String) result.get("message"));
|
|
|
/*Captcha _captcha = new Captcha();
|
|
|
_captcha.setCode("12345");
|
|
|
_captcha.setExpiresIn(10000);
|
|
|
_captcha.setInterval(60);
|
|
|
wlyyRedisVerifyCodeService.store(client_id, username, "12345", 100);
|
|
|
Oauth2Envelop<Captcha> oauth2Envelop = new Oauth2Envelop("captcha", 200, _captcha);
|
|
|
HttpHeaders headers = new HttpHeaders();
|
|
|
headers.set("Cache-Control", "no-store");
|
|
|
headers.set("Pragma", "no-cache");
|
|
|
return new ResponseEntity<>(verifyCode, headers, HttpStatus.OK);
|
|
|
return new ResponseEntity<>(oauth2Envelop, headers, HttpStatus.OK);*/
|
|
|
}
|
|
|
|
|
|
@RequestMapping(value = ServiceApi.Authentication.VerifyCodeValidate, method = RequestMethod.POST)
|
|
|
public ResponseEntity<Envelop> verifyCodeValidate(@RequestParam Map<String, String> parameters) throws Exception{
|
|
|
Envelop envelop = new Envelop();
|
|
|
HttpHeaders headers = new HttpHeaders();
|
|
|
headers.set("Cache-Control", "no-store");
|
|
|
headers.set("Pragma", "no-cache");
|
|
|
/**
|
|
|
* 验证验证码
|
|
|
* @param parameters
|
|
|
* @return
|
|
|
* @throws Exception
|
|
|
*/
|
|
|
@RequestMapping(value = "/oauth/captcha", method = RequestMethod.POST)
|
|
|
public ResponseEntity<Oauth2Envelop> captchaCheck (@RequestParam Map<String, String> parameters) throws Exception{
|
|
|
String client_id = parameters.get("client_id");
|
|
|
String username = parameters.get("username");
|
|
|
String verifyCode = parameters.get("verify_code");
|
|
|
if (StringUtils.isEmpty(verifyCode)){
|
|
|
envelop.setSuccessFlg(false);
|
|
|
envelop.setErrorMsg("验证码不能为空!");
|
|
|
return new ResponseEntity<>(envelop, headers, HttpStatus.OK);
|
|
|
String captcha = parameters.get("captcha");
|
|
|
if (StringUtils.isEmpty(client_id)) {
|
|
|
throw new InvalidRequestException("client_id");
|
|
|
}
|
|
|
boolean _verify = ehrRedisVerifyCodeService.verification(client_id, username, verifyCode);
|
|
|
if (_verify){
|
|
|
envelop.setSuccessFlg(true);
|
|
|
if (StringUtils.isEmpty(username)){
|
|
|
throw new InvalidRequestException("username");
|
|
|
}
|
|
|
if (StringUtils.isEmpty(captcha)){
|
|
|
throw new InvalidRequestException("captcha");
|
|
|
}
|
|
|
Oauth2Envelop<Boolean> oauth2Envelop;
|
|
|
if (wlyyRedisVerifyCodeService.verification(client_id, username, captcha)) {
|
|
|
oauth2Envelop = new Oauth2Envelop<>("验证码正确", 200, true);
|
|
|
} else {
|
|
|
envelop.setSuccessFlg(false);
|
|
|
envelop.setErrorMsg("请输入正确的验证码!");
|
|
|
oauth2Envelop = new Oauth2Envelop<>("验证码错误", 200, false);
|
|
|
}
|
|
|
return new ResponseEntity<>(envelop, headers, HttpStatus.OK);
|
|
|
}*/
|
|
|
HttpHeaders headers = new HttpHeaders();
|
|
|
headers.set("Cache-Control", "no-store");
|
|
|
headers.set("Pragma", "no-cache");
|
|
|
return new ResponseEntity<>(oauth2Envelop, headers, HttpStatus.OK);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
protected TokenGranter getTokenGranter() {
|
|
@ -308,27 +355,31 @@ public class WlyyLoginEndpoint extends AbstractEndpoint {
|
|
|
public ResponseEntity<Oauth2Envelop> handleException(Exception e) throws Exception {
|
|
|
LOG.info(e.getMessage(), e);
|
|
|
if (e instanceof UsernameNotFoundException) {
|
|
|
return handleOAuth2Exception(new Oauth2Envelop("用户未注册!", HttpStatus.UNAUTHORIZED.value()));
|
|
|
return handleOAuth2Exception(new Oauth2Envelop("用户未注册!", HttpStatus.UNAUTHORIZED.value()), e);
|
|
|
} else if (e instanceof NoSuchClientException) {
|
|
|
return handleOAuth2Exception(new Oauth2Envelop("应用未注册!", HttpStatus.UNAUTHORIZED.value()));
|
|
|
return handleOAuth2Exception(new Oauth2Envelop("应用未注册!", HttpStatus.UNAUTHORIZED.value()), e);
|
|
|
} else if (e instanceof InvalidGrantException) {
|
|
|
if (e.getMessage().equals("verify_code")) {
|
|
|
return handleOAuth2Exception(new Oauth2Envelop("验证码有误!", HttpStatus.UNAUTHORIZED.value()));
|
|
|
if (e.getMessage().contains("captcha")) {
|
|
|
return handleOAuth2Exception(new Oauth2Envelop("验证码有误!", HttpStatus.UNAUTHORIZED.value()), e);
|
|
|
}
|
|
|
return handleOAuth2Exception(new Oauth2Envelop("密码有误!", HttpStatus.UNAUTHORIZED.value()));
|
|
|
return handleOAuth2Exception(new Oauth2Envelop("密码有误!", HttpStatus.UNAUTHORIZED.value()), e);
|
|
|
} else if (e instanceof InvalidTokenException) {
|
|
|
return handleOAuth2Exception(new Oauth2Envelop("Token有误!", HttpStatus.UNAUTHORIZED.value()));
|
|
|
return handleOAuth2Exception(new Oauth2Envelop("Token有误!", HttpStatus.UNAUTHORIZED.value()), e);
|
|
|
} else if (e instanceof InvalidRequestException) {
|
|
|
return handleOAuth2Exception(new Oauth2Envelop("参数" + e.getMessage() + "缺失!", HttpStatus.UNAUTHORIZED.value()));
|
|
|
return handleOAuth2Exception(new Oauth2Envelop("参数" + e.getMessage() + "缺失!", HttpStatus.UNAUTHORIZED.value()), e);
|
|
|
} else if (e instanceof IllegalAccessException) {
|
|
|
return handleOAuth2Exception(new Oauth2Envelop("短信请求频率过快,请稍后再试!", -1), e);
|
|
|
} else if (e instanceof IllegalStateException) {
|
|
|
return handleOAuth2Exception(new Oauth2Envelop("短信网关请求失败!", -1), e);
|
|
|
}
|
|
|
return handleOAuth2Exception(new Oauth2Envelop(e.getMessage(), -1));
|
|
|
return handleOAuth2Exception(new Oauth2Envelop(e.getMessage(), -1), e);
|
|
|
}
|
|
|
|
|
|
private ResponseEntity<Oauth2Envelop> handleOAuth2Exception(Oauth2Envelop authenticationFailed) throws IOException {
|
|
|
private ResponseEntity<Oauth2Envelop> handleOAuth2Exception(Oauth2Envelop authenticationFailed, Exception e) throws IOException {
|
|
|
HttpHeaders headers = new HttpHeaders();
|
|
|
headers.set("Cache-Control", "no-store");
|
|
|
headers.set("Pragma", "no-cache");
|
|
|
headers.set("WWW-Authenticate", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, authenticationFailed.getMessage()));
|
|
|
headers.set("WWW-Authenticate", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, e.getMessage()));
|
|
|
ResponseEntity<Oauth2Envelop> response = new ResponseEntity<>(authenticationFailed, headers, HttpStatus.OK);
|
|
|
return response;
|
|
|
}
|