/**
* 自定义微信微信授权者
* @author kwj
* @date 2022/5/15
*/
public class WechatTokenGranter extends AbstractTokenGranter {
// 自定义授权方式为 wechat
private static final String GRANT_TYPE = "wechat";
private final AuthenticationManager authenticationManager;
public WechatTokenGranter(AuthenticationManager authenticationManager,
AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
}
protected WechatTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,
ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
super(tokenServices, clientDetailsService, requestFactory, grantType);
this.authenticationManager = authenticationManager;
}
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
Map parameters = new LinkedHashMap(tokenRequest.getRequestParameters());
String code = parameters.get("code");
// String encryptedData = parameters.get("encryptedData");
// String iv = parameters.get("iv");
// 移除后续无用参数
parameters.remove("code");
// parameters.remove("encryptedData");
// parameters.remove("iv");
Authentication userAuth = new WechatAuthenticationToken(code); // 未认证状态
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
try {
userAuth = this.authenticationManager.authenticate(userAuth); // 认证中
} catch (Exception e) {
throw new InvalidGrantException(e.getMessage());
}
if (userAuth != null && userAuth.isAuthenticated()) { // 认证成功
OAuth2Request storedOAuth2Request = this.getRequestFactory().createOAuth2Request(client, tokenRequest);
return new OAuth2Authentication(storedOAuth2Request, userAuth);
} else { // 认证失败
throw new InvalidGrantException("Could not authenticate code: " + code);
}
}
}
/**
* 自定义微信认证token
* @author kwj
*/
public class WechatAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final Object principal;
public WechatAuthenticationToken(String mobile) {
super(null);
this.principal = mobile;
setAuthenticated(false);
}
@JsonCreator
public WechatAuthenticationToken(@JsonProperty("principal") Object principal,
@JsonProperty("authorities") Collection extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
super.setAuthenticated(true);
}
@Override
public Object getPrincipal() {
return this.principal;
}
@Override
public Object getCredentials() {
return null;
}
@Override
@SneakyThrows
public void setAuthenticated(boolean isAuthenticated) {
if (isAuthenticated) {
throw new IllegalArgumentException(
"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
}
super.setAuthenticated(false);
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
}
}
/**
* 微信认证具体实现类
* @author kwj
*/
@Slf4j
@Component
public class WechatAuthenticationProvider implements AuthenticationProvider {
@Autowired
private RestTemplate restTemplate;
@Autowired
private WechatConfig wechatConfig;
@Autowired
private IWechatUserService webchatUserService;
@Autowired
private UserManager userManager;
@Override
@SneakyThrows
public Authentication authenticate(Authentication authentication) {
HttpServletRequest httpServletRequest = DciUtil.getHttpServletRequest();
WechatAuthenticationToken wechatAuthenticationToken = (WechatAuthenticationToken) authentication;
String code = wechatAuthenticationToken.getPrincipal().toString();
System.out.println(code);
// 调用微信请求接口,获取openId的值
String url = "https://api.weixin.qq.com/sns/jscode2session?appid={appid}&secret={secret}&js_code={code}&grant_type=authorization_code";
Map requestMap = new HashMap<>();
requestMap.put("appid", wechatConfig.getAppid());
requestMap.put("secret", wechatConfig.getSecret());
requestMap.put("code", code);
ResponseEntity responseEntity = restTemplate.getForEntity(url, String.class,requestMap);
JSONObject jsonObject= JSONObject.parseObject(responseEntity.getBody());
System.out.println(jsonObject);
String openId=jsonObject.getString("openid");
// String session_key=jsonObject.getString("session_key");
// 项目码
String projectCode = httpServletRequest.getParameter("projectCode");
if(StringUtils.isEmpty(openId)) {
throw new AuthenticationServiceException("Wechat Invalid Code");
}
WechatUser wechatUser = new WechatUser();
wechatUser.setProjectCode(projectCode);
wechatUser.setOpenid(openId);
WechatUser weUser = webchatUserService.getWechatUserByOpenId(wechatUser);
// 如果是第一次登录,进行信息添加
if(weUser == null) {
wechatUser.setCreateTime(new Date());
webchatUserService.save(wechatUser);
httpServletRequest.setAttribute("wxid", wechatUser.getWxid());
throw new AuthenticationServiceException("Wechat No Bind Phone");
} else {
String phone = weUser.getPhone();
if(StringUtils.isEmpty(phone)) {
httpServletRequest.setAttribute("wxid", weUser.getWxid());
throw new AuthenticationServiceException("Wechat No Bind Phone");
}
}
// 预留权限控制,防止以后需要
String permissions = "";
List grantedAuthorities = AuthorityUtils.NO_AUTHORITIES;
if (StringUtils.isNotBlank(permissions)) {
grantedAuthorities = AuthorityUtils.commaSeparatedStringToAuthorityList(permissions);
}
DciAuthUser authUser = new DciAuthUser(weUser.getPhone(), "", grantedAuthorities);
BeanUtils.copyProperties(weUser, authUser);
authUser.setCurrProjectCode("2");
WechatAuthenticationToken authenticationToken = new WechatAuthenticationToken(authUser,
authUser.getAuthorities());
LinkedHashMap
在继承AuthorizationServerConfigurerAdapter的配置类中添加如下代码:
/**
* 创建grant_type列表
* @param endpoints
* @return
*/
private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) {
List list = new ArrayList<>();
// 这里配置密码模式
if (authenticationManager != null) {
list.add(new ResourceOwnerPasswordTokenGranter(authenticationManager,
endpoints.getTokenServices(),
endpoints.getClientDetailsService(),
endpoints.getOAuth2RequestFactory()));
}
//刷新token模式、
list.add(new RefreshTokenGranter
(endpoints.getTokenServices(),
endpoints.getClientDetailsService(),
endpoints.getOAuth2RequestFactory()));
//授权码模式、
list.add(new AuthorizationCodeTokenGranter(
endpoints.getTokenServices(),
endpoints.getAuthorizationCodeServices(),
endpoints.getClientDetailsService(),
endpoints.getOAuth2RequestFactory()));
//、简化模式
list.add(new ImplicitTokenGranter(
endpoints.getTokenServices(),
endpoints.getClientDetailsService(),
endpoints.getOAuth2RequestFactory()));
//客户端模式
list.add(new ClientCredentialsTokenGranter(
endpoints.getTokenServices(),
endpoints.getClientDetailsService(),
endpoints.getOAuth2RequestFactory()));
// 以上为oauth自带的认证,需再次加一遍
// 微信小程序验证模式(自定义)、
list.add(new WechatTokenGranter(
authenticationManager,
endpoints.getTokenServices(),
endpoints.getClientDetailsService(),
endpoints.getOAuth2RequestFactory()));
return new CompositeTokenGranter(list);
}
在继承WebSecurityConfigurerAdapter的配置类中添加如下代码:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 配置具体的认证类
auth.authenticationProvider(wechatAuthenticationProvider)
.userDetailsService(userDetailService)
.passwordEncoder(passwordEncoder);
}
微信端发起请求时,除传入需要的参数外,还需传入参数grant_type:'wechat',此处是与上面步骤一定义的名称是一致的。