This commit is contained in:
有来技术 2021-09-29 23:55:27 +08:00
parent c93750d73c
commit 54747673ab
20 changed files with 152 additions and 232 deletions

View File

@ -1,14 +1,13 @@
package com.youlai.mall.ums.pojo.dto; package com.youlai.mall.ums.pojo.dto;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@Data @Data
@AllArgsConstructor
public class MemberAuthDTO { public class MemberAuthDTO {
private Long id; private Long userId;
private String username; private String openId;
private Integer status; private Integer status;
private String avatar;
private String nickname;
} }

View File

@ -65,13 +65,14 @@ public class MemberController {
@ApiImplicitParam(name = "openid", value = "微信身份唯一标识", required = true, paramType = "path", dataType = "String") @ApiImplicitParam(name = "openid", value = "微信身份唯一标识", required = true, paramType = "path", dataType = "String")
@GetMapping("/openid/{openid}") @GetMapping("/openid/{openid}")
public Result<MemberAuthDTO> getByOpenid(@PathVariable String openid) { public Result<MemberAuthDTO> getByOpenid(@PathVariable String openid) {
UmsMember member = iUmsMemberService.getOne(new LambdaQueryWrapper<UmsMember>().eq(UmsMember::getOpenid, openid)); UmsMember member = iUmsMemberService.getOne(new LambdaQueryWrapper<UmsMember>().eq(UmsMember::getOpenid, openid)
.select(UmsMember::getId,UmsMember::getOpenid,UmsMember::getStatus)
);
if (member == null) { if (member == null) {
return Result.failed(ResultCode.USER_NOT_EXIST); return Result.failed(ResultCode.USER_NOT_EXIST);
} }
// 会员认证信息 // 会员认证信息
MemberAuthDTO memberAuth = new MemberAuthDTO(); MemberAuthDTO memberAuth = new MemberAuthDTO(member.getId(),member.getOpenid(),member.getStatus());
BeanUtil.copyProperties(member, memberAuth);
return Result.success(memberAuth); return Result.success(memberAuth);
} }

View File

@ -107,7 +107,8 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
@Override @Override
public UserAuthDTO getByUsername(String username) { public UserAuthDTO getByUsername(String username) {
return this.baseMapper.getByUsername(username); UserAuthDTO userAuthInfo = this.baseMapper.getByUsername(username);
return userAuthInfo;
} }
} }

View File

@ -31,8 +31,19 @@
email,gmt_create,gmt_modified, email,gmt_create,gmt_modified,
deleted deleted
</sql> </sql>
<select id="getByUsername" resultMap="BaseResultMap">
select t1.id, t1.username, t1.nickname, t1.password, t1.status, t3.code roleCode <resultMap id="UserAuthMap" type="com.youlai.admin.pojo.dto.UserAuthDTO">
<id property="userId" column="id" jdbcType="BIGINT"/>
<result property="username" column="username" jdbcType="VARCHAR"/>
<result property="password" column="password" jdbcType="VARCHAR"/>
<result property="status" column="status" jdbcType="BOOLEAN"/>
<collection property="roles" ofType="string" javaType="list">
<result column="roleCode"></result>
</collection>
</resultMap>
<select id="getByUsername" resultMap="UserAuthMap">
select t1.id userId, t1.username, t1.nickname, t1.password, t1.status, t3.code roleCode
from sys_user t1, from sys_user t1,
sys_user_role t2, sys_user_role t2,
sys_role t3 sys_role t3

View File

@ -113,5 +113,4 @@
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -12,6 +12,8 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableDiscoveryClient @EnableDiscoveryClient
public class AuthApplication { public class AuthApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args); SpringApplication.run(AuthApplication.class, args);
} }
} }

View File

@ -5,9 +5,6 @@ import cn.hutool.json.JSONUtil;
import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey; import com.nimbusds.jose.jwk.RSAKey;
import com.youlai.auth.common.enums.OAuthClientEnum; import com.youlai.auth.common.enums.OAuthClientEnum;
import com.youlai.auth.domain.OAuthToken;
import com.youlai.auth.domain.UserInfo;
import com.youlai.auth.service.IAuthService;
import com.youlai.common.constant.AuthConstants; import com.youlai.common.constant.AuthConstants;
import com.youlai.common.result.Result; import com.youlai.common.result.Result;
import com.youlai.common.web.util.JwtUtils; import com.youlai.common.web.util.JwtUtils;
@ -37,15 +34,14 @@ import java.util.concurrent.TimeUnit;
public class OAuthController { public class OAuthController {
private TokenEndpoint tokenEndpoint; private TokenEndpoint tokenEndpoint;
private IAuthService wechatAuthService;
private RedisTemplate redisTemplate; private RedisTemplate redisTemplate;
private KeyPair keyPair; private KeyPair keyPair;
@ApiOperation(value = "OAuth2认证", notes = "login") @ApiOperation(value = "OAuth2认证", notes = "login")
@ApiImplicitParams({ @ApiImplicitParams({
@ApiImplicitParam(name = "grant_type", defaultValue = "password", value = "授权模式", required = true), @ApiImplicitParam(name = "grant_type", defaultValue = "password", value = "授权模式", required = true),
@ApiImplicitParam(name = "client_id", value = "Oauth2客户端ID(新版本需放置请求头)", required = true), @ApiImplicitParam(name = "client_id", value = "Oauth2客户端ID", required = true),
@ApiImplicitParam(name = "client_secret", value = "Oauth2客户端秘钥(新版本需放置请求头)", required = true), @ApiImplicitParam(name = "client_secret", value = "Oauth2客户端秘钥", required = true),
@ApiImplicitParam(name = "refresh_token", value = "刷新token"), @ApiImplicitParam(name = "refresh_token", value = "刷新token"),
@ApiImplicitParam(name = "username", defaultValue = "admin", value = "登录用户名"), @ApiImplicitParam(name = "username", defaultValue = "admin", value = "登录用户名"),
@ApiImplicitParam(name = "password", defaultValue = "123456", value = "登录密码") @ApiImplicitParam(name = "password", defaultValue = "123456", value = "登录密码")
@ -74,14 +70,6 @@ public class OAuthController {
} }
} }
@ApiOperation(value = "微信授权登录")
@ApiImplicitParam(name = "code", value = "小程序授权code", paramType = "path")
@PostMapping("/wechat-token")
public Result wechatLogin(@PathVariable String code, @RequestBody UserInfo userInfo) {
OAuthToken token = wechatAuthService.login(code, userInfo);
return Result.success(token);
}
@ApiOperation(value = "注销", notes = "logout") @ApiOperation(value = "注销", notes = "logout")
@DeleteMapping("/logout") @DeleteMapping("/logout")
public Result logout() { public Result logout() {

View File

@ -1,37 +0,0 @@
package com.youlai.auth.domain;
import com.youlai.auth.common.jwt.JwtPayloadBuilder;
import lombok.*;
import java.util.Set;
/**
* 描述: [自定义token]
* 创建时间: 2021/6/8
*
* @author hxr
* @version 1.0.0
* @update [序号][日期YYYY-MM-DD] [更改人姓名][变更描述]
*/
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Data
public class OAuthToken {
private String access_token;
private String token_type = "bearer";
public OAuthToken accessToken(String accessToken) {
this.access_token = accessToken;
return this;
}
public OAuthToken tokenType(String tokenType) {
this.token_type = tokenType;
return this;
}
}

View File

@ -3,9 +3,9 @@ package com.youlai.auth.security.config;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.http.HttpStatus; import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.youlai.auth.security.core.userdetails.system.SysUserDetails;
import com.youlai.auth.security.core.clientdetails.ClientDetailsServiceImpl; import com.youlai.auth.security.core.clientdetails.ClientDetailsServiceImpl;
import com.youlai.auth.security.core.userdetails.system.SysUserDetailsServiceImpl; import com.youlai.auth.security.core.userdetails.system.SysUserDetails;
import com.youlai.auth.security.extension.wechat.WechatTokenGranter;
import com.youlai.common.result.Result; import com.youlai.common.result.Result;
import com.youlai.common.result.ResultCode; import com.youlai.common.result.ResultCode;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@ -16,14 +16,13 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain; import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
@ -42,7 +41,7 @@ import java.util.*;
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private AuthenticationManager authenticationManager; private AuthenticationManager authenticationManager;
private SysUserDetailsServiceImpl sysUserDetailsService;
private ClientDetailsServiceImpl clientDetailsService; private ClientDetailsServiceImpl clientDetailsService;
/** /**
@ -59,39 +58,29 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
*/ */
@Override @Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) { public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
// Token增强
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
List<TokenEnhancer> tokenEnhancers = new ArrayList<>(); List<TokenEnhancer> tokenEnhancers = new ArrayList<>();
tokenEnhancers.add(tokenEnhancer()); tokenEnhancers.add(tokenEnhancer());
tokenEnhancers.add(jwtAccessTokenConverter()); tokenEnhancers.add(jwtAccessTokenConverter());
tokenEnhancerChain.setTokenEnhancers(tokenEnhancers); tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);
// 添加自定义授权模式
List<TokenGranter> granterList = new ArrayList<>(Arrays.asList(endpoints.getTokenGranter()));
granterList.add(new WechatTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), authenticationManager));
CompositeTokenGranter compositeTokenGranter = new CompositeTokenGranter(granterList);
endpoints endpoints
.authenticationManager(authenticationManager) .authenticationManager(authenticationManager)
.accessTokenConverter(jwtAccessTokenConverter()) .accessTokenConverter(jwtAccessTokenConverter())
.tokenEnhancer(tokenEnhancerChain) .tokenEnhancer(tokenEnhancerChain)
// .userDetailsService(userDetailsService) .tokenGranter(compositeTokenGranter)
// refresh token有两种使用方式重复使用(true)非重复使用(false)默认为true // refresh token有两种使用方式重复使用(true)非重复使用(false)默认为true
// 1 重复使用access token过期刷新时 refresh token过期时间未改变仍以初次生成的时间为准 // 1 重复使用access token过期刷新时 refresh token过期时间未改变仍以初次生成的时间为准
// 2 非重复使用access token过期刷新时 refresh token过期时间延续在refresh token有效期内刷新便永不失效达到无需再次登录的目的 // 2 非重复使用access token过期刷新时 refresh token过期时间延续在refresh token有效期内刷新便永不失效达到无需再次登录的目的
.reuseRefreshTokens(true); .reuseRefreshTokens(true);
} }
/**
* 重写 DaoAuthenticationProvider
*
* @return
*/
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setHideUserNotFoundExceptions(false); // 是否隐藏用户不存在异常默认:true-隐藏false-抛出异常
provider.setUserDetailsService(sysUserDetailsService);
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
/** /**
* 使用非对称加密算法对token签名 * 使用非对称加密算法对token签名
*/ */
@ -103,7 +92,7 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
} }
/** /**
* 从classpath下的密钥库中获取密钥对(公钥+私钥) * 密钥库中获取密钥对(公钥+私钥)
*/ */
@Bean @Bean
public KeyPair keyPair() { public KeyPair keyPair() {
@ -128,17 +117,6 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
} }
/**
* 密码编码器
* <p>
* 委托方式根据密码的前缀选择对应的encoder例如{bcypt}前缀->标识BCYPT算法加密{noop}->标识不使用任何加密即明文的方式
* 密码判读 DaoAuthenticationProvider#additionalAuthenticationChecks
*/
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
/** /**
* 自定义认证异常响应数据 * 自定义认证异常响应数据
*/ */

View File

@ -1,18 +1,31 @@
package com.youlai.auth.security.config; package com.youlai.auth.security.config;
import com.youlai.auth.security.core.userdetails.member.MemberUserDetailsServiceImpl;
import com.youlai.auth.security.core.userdetails.system.SysUserDetailsServiceImpl;
import com.youlai.auth.security.extension.wechat.WechatAuthenticationProvider;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@Slf4j @Slf4j
@RequiredArgsConstructor
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService sysUserDetailsService;
private final UserDetailsService memberUserDetailsService;
@Override @Override
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
http http
@ -25,7 +38,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
} }
/** /**
* 认证管理中心 * 认证管理对象
* *
* @return * @return
* @throws Exception * @throws Exception
@ -35,5 +48,40 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
return super.authenticationManagerBean(); return super.authenticationManagerBean();
} }
@Override
public void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(wechatAuthenticationProvider())
.authenticationProvider(daoAuthenticationProvider());
}
@Bean
public WechatAuthenticationProvider wechatAuthenticationProvider() {
WechatAuthenticationProvider provider = new WechatAuthenticationProvider();
provider.setUserDetailsService(memberUserDetailsService);
return provider;
}
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setHideUserNotFoundExceptions(false); // 是否隐藏用户不存在异常默认:true-隐藏false-抛出异常
provider.setUserDetailsService(sysUserDetailsService);
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
/**
* 密码编码器
* <p>
* 委托方式根据密码的前缀选择对应的encoder例如{bcypt}前缀->标识BCYPT算法加密{noop}->标识不使用任何加密即明文的方式
* 密码判读 DaoAuthenticationProvider#additionalAuthenticationChecks
*/
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
} }

View File

@ -20,6 +20,7 @@ import java.util.Collection;
public class MemberUserDetails implements UserDetails { public class MemberUserDetails implements UserDetails {
private Long userId; private Long userId;
private String openId;
private Boolean enabled; private Boolean enabled;
private Collection<SimpleGrantedAuthority> authorities; private Collection<SimpleGrantedAuthority> authorities;
@ -31,11 +32,11 @@ public class MemberUserDetails implements UserDetails {
* @param member 小程序会员用户认证信息 * @param member 小程序会员用户认证信息
*/ */
public MemberUserDetails(MemberAuthDTO member) { public MemberUserDetails(MemberAuthDTO member) {
this.setUserId(member.getId()); this.setUserId(member.getUserId());
this.setOpenId(member.getOpenId());
this.setEnabled(GlobalConstants.STATUS_YES.equals(member.getStatus())); this.setEnabled(GlobalConstants.STATUS_YES.equals(member.getStatus()));
} }
@Override @Override
public Collection<? extends GrantedAuthority> getAuthorities() { public Collection<? extends GrantedAuthority> getAuthorities() {
return this.authorities; return this.authorities;
@ -48,7 +49,7 @@ public class MemberUserDetails implements UserDetails {
@Override @Override
public String getUsername() { public String getUsername() {
return null; return this.openId;
} }
@Override @Override

View File

@ -1,10 +1,13 @@
package com.youlai.auth.security.core.userdetails.member; package com.youlai.auth.security.core.userdetails.member;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.youlai.common.result.Result; import com.youlai.common.result.Result;
import com.youlai.common.result.ResultCode; import com.youlai.common.result.ResultCode;
import com.youlai.mall.ums.api.MemberFeignClient; import com.youlai.mall.ums.api.MemberFeignClient;
import com.youlai.mall.ums.pojo.dto.MemberAuthDTO; import com.youlai.mall.ums.pojo.dto.MemberAuthDTO;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AccountExpiredException; import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.DisabledException; import org.springframework.security.authentication.DisabledException;
@ -19,21 +22,33 @@ import org.springframework.stereotype.Service;
* *
* @author <a href="mailto:xianrui0365@163.com">xianrui</a> * @author <a href="mailto:xianrui0365@163.com">xianrui</a>
*/ */
@Service @Service("memberUserDetailsService")
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class MemberUserDetailsServiceImpl implements UserDetailsService { public class MemberUserDetailsServiceImpl implements UserDetailsService {
private final MemberFeignClient memberFeignClient; private final MemberFeignClient memberFeignClient;
private final WxMaService wxMaService;
@SneakyThrows
@Override @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { public UserDetails loadUserByUsername(String code) throws UsernameNotFoundException {
MemberUserDetails userDetails = null; MemberUserDetails userDetails = null;
Result<MemberAuthDTO> result = memberFeignClient.loadUserByOpenId(username); WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
String openid = sessionInfo.getOpenid();
Result<MemberAuthDTO> result = memberFeignClient.loadUserByOpenId(openid);
if (Result.isSuccess(result)) { if (Result.isSuccess(result)) {
MemberAuthDTO member = result.getData(); MemberAuthDTO member = result.getData();
if (null != member) { if (null != member) {
userDetails = new MemberUserDetails(member); userDetails = new MemberUserDetails(member);
} else { // 微信用户不存在同步微信用户信息注册为小程序会员
//wxMaService.getUserService().getUserInfo()
} }
} }
if (userDetails == null) { if (userDetails == null) {

View File

@ -52,18 +52,6 @@ public class SysUserDetails implements UserDetails {
} }
} }
/**
* 小程序会员用户体系
*
* @param member 小程序会员用户认证信息
*/
public SysUserDetails(MemberAuthDTO member) {
this.setUserId(member.getId());
this.setUsername(member.getUsername());
this.setEnabled(GlobalConstants.STATUS_YES.equals(member.getStatus()));
}
@Override @Override
public Collection<? extends GrantedAuthority> getAuthorities() { public Collection<? extends GrantedAuthority> getAuthorities() {
return this.authorities; return this.authorities;

View File

@ -19,7 +19,7 @@ import org.springframework.stereotype.Service;
* *
* @author <a href="mailto:xianrui0365@163.com">xianrui</a> * @author <a href="mailto:xianrui0365@163.com">xianrui</a>
*/ */
@Service @Service("sysUserDetailsService")
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class SysUserDetailsServiceImpl implements UserDetailsService { public class SysUserDetailsServiceImpl implements UserDetailsService {

View File

@ -1,14 +1,9 @@
package com.youlai.auth.domain; package com.youlai.auth.security.extension.wechat;
import lombok.Data;
/** /**
* 描述: [微信用户信息] * @author <a href="mailto:xianrui0365@163.com">xianrui</a>
* 创建时间: 2021/6/8 * @date 2021/9/29
*
* @author hxr
*/ */
@Data
public class UserInfo { public class UserInfo {
private String avatarUrl; private String avatarUrl;

View File

@ -1,20 +1,25 @@
package com.youlai.auth.security.extension.wechat; package com.youlai.auth.security.extension.wechat;
import com.youlai.auth.security.core.userdetails.member.MemberUserDetailsServiceImpl; import lombok.Data;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
/** /**
* @author <a href="mailto:xianrui0365@163.com">xianrui</a> * @author <a href="mailto:xianrui0365@163.com">xianrui</a>
* @date 2021/9/25 * @date 2021/9/25
*/ */
@Data
public class WechatAuthenticationProvider implements AuthenticationProvider { public class WechatAuthenticationProvider implements AuthenticationProvider {
private MemberUserDetailsServiceImpl memberUserDetailsService; private UserDetailsService userDetailsService;
/** /**
* 用户认证
*
* @param authentication * @param authentication
* @return * @return
* @throws AuthenticationException * @throws AuthenticationException
@ -22,10 +27,10 @@ public class WechatAuthenticationProvider implements AuthenticationProvider {
@Override @Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException { public Authentication authenticate(Authentication authentication) throws AuthenticationException {
WechatAuthenticationToken authenticationToken = (WechatAuthenticationToken) authentication; WechatAuthenticationToken authenticationToken = (WechatAuthenticationToken) authentication;
String openId = (String) authenticationToken.getPrincipal(); String code = (String) authenticationToken.getPrincipal();
UserDetails userDetails = memberUserDetailsService.loadUserByUsername(openId); UserDetails userDetails = userDetailsService.loadUserByUsername(code);
WechatAuthenticationToken result = new WechatAuthenticationToken(openId, userDetails.getAuthorities()); WechatAuthenticationToken result = new WechatAuthenticationToken(userDetails.getUsername(), userDetails.getAuthorities());
result.setDetails(authentication.getDetails()); result.setDetails(authentication.getDetails());
return result; return result;
} }

View File

@ -15,12 +15,23 @@ public class WechatAuthenticationToken extends AbstractAuthenticationToken {
private final Object principal; private final Object principal;
/**
* 账号校验之前的token构建
*
* @param principal
*/
public WechatAuthenticationToken(Object principal) { public WechatAuthenticationToken(Object principal) {
super(null); super(null);
this.principal = principal; this.principal = principal;
setAuthenticated(false); setAuthenticated(false);
} }
/**
* 账号校验成功之后的token构建
*
* @param principal
* @param authorities
*/
public WechatAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) { public WechatAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
super(authorities); super(authorities);
this.principal = principal; this.principal = principal;
@ -37,7 +48,6 @@ public class WechatAuthenticationToken extends AbstractAuthenticationToken {
return this.principal; return this.principal;
} }
@Override @Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
Assert.isTrue(!isAuthenticated, "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead"); Assert.isTrue(!isAuthenticated, "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");

View File

@ -19,8 +19,8 @@ public class WechatTokenGranter extends AbstractTokenGranter {
private static final String GRANT_TYPE = "wechat"; private static final String GRANT_TYPE = "wechat";
private final AuthenticationManager authenticationManager; private final AuthenticationManager authenticationManager;
protected WechatTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType, AuthenticationManager authenticationManager) { public WechatTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, AuthenticationManager authenticationManager) {
super(tokenServices, clientDetailsService, requestFactory, grantType); super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
this.authenticationManager = authenticationManager; this.authenticationManager = authenticationManager;
} }
@ -28,21 +28,27 @@ public class WechatTokenGranter extends AbstractTokenGranter {
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) { protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
Map<String, String> parameters = new LinkedHashMap(tokenRequest.getRequestParameters()); Map<String, String> parameters = new LinkedHashMap(tokenRequest.getRequestParameters());
String openId = parameters.get("openId"); String code = parameters.get("code");
Authentication userAuth = new WechatAuthenticationToken(openId); String rawData = parameters.get("rawData");
parameters.remove("code");
parameters.remove("rawData");
Authentication userAuth = new WechatAuthenticationToken(code); // 未认证状态
((AbstractAuthenticationToken) userAuth).setDetails(parameters); ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
try { try {
userAuth = this.authenticationManager.authenticate(userAuth); // 参数校验 userAuth = this.authenticationManager.authenticate(userAuth); // 认证中
} catch (Exception e) { } catch (Exception e) {
throw new InvalidGrantException(e.getMessage()); throw new InvalidGrantException(e.getMessage());
} }
if (userAuth != null && userAuth.isAuthenticated()) { if (userAuth != null && userAuth.isAuthenticated()) { // 认证成功
OAuth2Request storedOAuth2Request = this.getRequestFactory().createOAuth2Request(client, tokenRequest); OAuth2Request storedOAuth2Request = this.getRequestFactory().createOAuth2Request(client, tokenRequest);
return new OAuth2Authentication(storedOAuth2Request, userAuth); return new OAuth2Authentication(storedOAuth2Request, userAuth);
} else { } else { // 认证失败
throw new InvalidGrantException("Could not authenticate user: " + openId); throw new InvalidGrantException("Could not authenticate code: " + code);
} }
} }
} }

View File

@ -1,19 +0,0 @@
package com.youlai.auth.service;
import com.youlai.auth.domain.OAuthToken;
import com.youlai.auth.domain.UserInfo;
import java.util.Map;
/**
* 描述: [类型描述]
* 创建时间: 2021/6/8
*
* @author hxr
* @version 1.0.0
* @update [序号][日期YYYY-MM-DD] [更改人姓名][变更描述]
*/
public interface IAuthService {
OAuthToken login(String code, UserInfo userInfo);
}

View File

@ -1,71 +0,0 @@
package com.youlai.auth.service.impl;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import com.youlai.auth.common.jwt.JwtGenerator;
import com.youlai.auth.domain.OAuthToken;
import com.youlai.auth.domain.UserInfo;
import com.youlai.auth.service.IAuthService;
import com.youlai.common.constant.AuthConstants;
import com.youlai.common.result.Result;
import com.youlai.common.result.ResultCode;
import com.youlai.mall.ums.api.MemberFeignClient;
import com.youlai.mall.ums.pojo.entity.UmsMember;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @author haoxr
* @description 微信小程序认证接口
* @createTime 2021/5/20 23:37
*/
@Service
@AllArgsConstructor
public class WechatAuthServiceImpl implements IAuthService {
private MemberFeignClient memberFeignClient;
private WxMaService wxMaService;
private JwtGenerator jwtGenerator;
@SneakyThrows
@Override
public OAuthToken login(String code, UserInfo userInfo) {
// 微信小程序的授权code获取openid
WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
String openid = sessionInfo.getOpenid();
Result<UmsMember> result = memberFeignClient.getByOpenid(openid);
UmsMember member;
if (ResultCode.USER_NOT_EXIST.getCode().equals(result.getCode())) {
// 用户不存在注册成为新用户
member = new UmsMember();
BeanUtil.copyProperties(userInfo, member);
member.setOpenid(openid);
Result<Long> addRes = memberFeignClient.add(member);
Assert.isTrue(ResultCode.SUCCESS.getCode().equals(addRes.getCode()), "微信用户注册失败");
member.setId(addRes.getData()); // 新增后有了会员ID
} else {
member = result.getData();
}
// 自定义JWT生成
// 1. JWT授权一般存放用户的角色标识用于资源服务器网关鉴权
Set<String> authorities = new HashSet<>();
// 2. JWT增强携带用户ID等信息
Map<String, String> additional = new HashMap<>();
additional.put(AuthConstants.USER_ID_KEY, Convert.toStr(member.getId()));
String accessToken = jwtGenerator.createAccessToken(authorities, additional);
OAuthToken token = new OAuthToken().accessToken(accessToken);
return token;
}
}