Commit 31bba7d29842affd11ad2628ec46f5517edbff48

Authored by 云中非
1 parent 0134498f

refactor: 第三方接口

1、使用第三方平台ID登录
2、与系统用户绑定后返回访问令牌
... ... @@ -71,10 +71,14 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
71 71 public static final String WEBJARS_ENTRY_POINT = "/webjars/**";
72 72 public static final String DEVICE_API_ENTRY_POINT = "/api/v1/**";
73 73 public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login";
  74 +
  75 + //Thingskit function
74 76 public static final String CODE_BASED_LOGIN_ENTRY_POINT = "/api/yt/auth/code/login";
  77 + public static final String[] YT_NOT_AUTH_API = new String[]{"/api/yt/auth/code/login","/api/yt/third/bind","/api/yt/third/login/*", "/api/yt/noauth/**"};
  78 +
75 79 public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public";
76 80 public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token";
77   - protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/assets/**", "/static/**", "/api/noauth/**", "/webjars/**", "/api/license/**", "/api/yt/noauth/**"};
  81 + protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/assets/**", "/static/**", "/api/noauth/**", "/webjars/**", "/api/license/**"};
78 82 public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**";
79 83 public static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**";
80 84
... ... @@ -142,8 +146,12 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
142 146
143 147 protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception {
144 148 List<String> pathsToSkip = new ArrayList<>(Arrays.asList(NON_TOKEN_BASED_AUTH_ENTRY_POINTS));
145   - pathsToSkip.addAll(Arrays.asList(WS_TOKEN_BASED_AUTH_ENTRY_POINT, TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT,CODE_BASED_LOGIN_ENTRY_POINT,
  149 + pathsToSkip.addAll(Arrays.asList(WS_TOKEN_BASED_AUTH_ENTRY_POINT, TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT,
146 150 PUBLIC_LOGIN_ENTRY_POINT, DEVICE_API_ENTRY_POINT, WEBJARS_ENTRY_POINT));
  151 +
  152 + //Thingskit function
  153 + pathsToSkip.addAll(Arrays.asList(YT_NOT_AUTH_API));
  154 +
147 155 SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT);
148 156 JwtTokenAuthenticationProcessingFilter filter
149 157 = new JwtTokenAuthenticationProcessingFilter(failureHandler, jwtHeaderTokenExtractor, matcher);
... ... @@ -209,7 +217,10 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
209 217 .antMatchers(WEBJARS_ENTRY_POINT).permitAll() // Webjars
210 218 .antMatchers(DEVICE_API_ENTRY_POINT).permitAll() // Device HTTP Transport API
211 219 .antMatchers(FORM_BASED_LOGIN_ENTRY_POINT).permitAll() // Login end-point
212   - .antMatchers(CODE_BASED_LOGIN_ENTRY_POINT).permitAll() // SmsCode Login end-point
  220 +
  221 + //Thingskit function
  222 + .antMatchers(YT_NOT_AUTH_API).permitAll() // SmsCode Login end-point
  223 +
213 224 .antMatchers(PUBLIC_LOGIN_ENTRY_POINT).permitAll() // Public login end-point
214 225 .antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll() // Token refresh end-point
215 226 .antMatchers(NON_TOKEN_BASED_AUTH_ENTRY_POINTS).permitAll() // static resources, user activation and password reset end-points
... ...
... ... @@ -4,10 +4,16 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
4 4 import io.swagger.annotations.Api;
5 5 import io.swagger.annotations.ApiOperation;
6 6 import lombok.RequiredArgsConstructor;
  7 +import org.jetbrains.annotations.NotNull;
7 8 import org.springframework.security.access.prepost.PreAuthorize;
8 9 import org.springframework.validation.annotation.Validated;
9 10 import org.springframework.web.bind.annotation.*;
  11 +import org.thingsboard.server.common.data.StringUtils;
  12 +import org.thingsboard.server.common.data.User;
10 13 import org.thingsboard.server.common.data.exception.ThingsboardException;
  14 +import org.thingsboard.server.common.data.id.UserId;
  15 +import org.thingsboard.server.common.data.security.UserCredentials;
  16 +import org.thingsboard.server.common.data.security.model.JwtToken;
11 17 import org.thingsboard.server.common.data.yunteng.common.DeleteGroup;
12 18 import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO;
13 19 import org.thingsboard.server.common.data.yunteng.dto.YtOpinionDTO;
... ... @@ -20,6 +26,15 @@ import org.thingsboard.server.dao.yunteng.entities.YtOpinionEntity;
20 26 import org.thingsboard.server.dao.yunteng.entities.YtThirdUserEntity;
21 27 import org.thingsboard.server.dao.yunteng.service.YtOpinionService;
22 28 import org.thingsboard.server.dao.yunteng.service.YtThirdPlatformService;
  29 +import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository;
  30 +import org.thingsboard.server.service.security.auth.rest.LoginResponse;
  31 +import org.thingsboard.server.service.security.model.JwtTokenPair;
  32 +import org.thingsboard.server.service.security.model.SecurityUser;
  33 +import org.thingsboard.server.service.security.model.UserPrincipal;
  34 +import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
  35 +import org.thingsboard.server.service.security.permission.Operation;
  36 +
  37 +import java.util.UUID;
23 38
24 39 import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.*;
25 40
... ... @@ -32,6 +47,8 @@ import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.
32 47 @RequiredArgsConstructor
33 48 public class YtThirdPlatformController extends BaseController {
34 49
  50 + private final JwtTokenFactory tokenFactory;
  51 + private final RefreshTokenRepository refreshTokenRepository;
35 52 private final YtThirdPlatformService thirdService;
36 53
37 54 @GetMapping(params = {PAGE_SIZE, PAGE})
... ... @@ -47,14 +64,15 @@ public class YtThirdPlatformController extends BaseController {
47 64
48 65
49 66 IPage<YtThirdUserEntity> pageInfrom = thirdService.getPage(page, pageSize, orderBy, orderType);
50   - return thirdService.pageDatas(pageInfrom,platformName,name);
  67 + return thirdService.pageDatas(pageInfrom, platformName, name);
51 68 }
52 69
53   - @PostMapping
54   - @ApiOperation("绑定|编辑")
55   - public YtThirdUserDTO saveOrUpdateAlarmProfile(
  70 + @PostMapping("bind")
  71 + @ApiOperation("绑定")
  72 + public JwtTokenPair saveOrUpdateAlarmProfile(
56 73 @Validated @RequestBody YtThirdUserDTO dto) throws ThingsboardException {
57   - return thirdService.saveOrUpdate(dto);
  74 + String tbUserId = thirdService.saveOrUpdate(dto);
  75 + return buildJwtToken(tbUserId);
58 76 }
59 77
60 78 @DeleteMapping
... ... @@ -65,5 +83,30 @@ public class YtThirdPlatformController extends BaseController {
65 83 return thirdService.deleteDataByIds(deleteDTO);
66 84 }
67 85
  86 + @GetMapping("login/{thirdId}")
  87 + @ApiOperation("第三方登录")
  88 + public JwtTokenPair login(@PathVariable("thirdId") String thirdId)
  89 + throws ThingsboardException {
  90 + String tbUserId = thirdService.login(thirdId);
  91 + return buildJwtToken(tbUserId);
  92 + }
  93 +
  94 + @NotNull
  95 + private JwtTokenPair buildJwtToken(String tbUserId) {
  96 + String accessToken="";
  97 + String refreshToken="";
  98 + if(StringUtils.isNotEmpty(tbUserId)){
  99 + UserId userId = new UserId(UUID.fromString(tbUserId));
  100 + User user = userService.findUserById(null, userId);
  101 + UserCredentials credentials = userService.findUserCredentialsByUserId(user.getTenantId(), userId);
  102 + UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail());
  103 + SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal);
  104 + accessToken = tokenFactory.createAccessJwtToken(securityUser).getToken();
  105 + refreshToken = refreshTokenRepository.requestRefreshToken(securityUser).getToken();
  106 +
  107 + }
  108 + return new JwtTokenPair(accessToken, refreshToken);
  109 + }
  110 +
68 111
69 112 }
... ...
... ... @@ -3,6 +3,7 @@ package org.thingsboard.server.common.data.yunteng.dto;
3 3 import io.swagger.annotations.ApiModelProperty;
4 4 import lombok.Data;
5 5 import lombok.EqualsAndHashCode;
  6 +import org.thingsboard.server.common.data.yunteng.enums.LoginMethodEnum;
6 7 import org.thingsboard.server.common.data.yunteng.enums.ThirdPlatformEnum;
7 8
8 9 import javax.validation.constraints.NotEmpty;
... ... @@ -26,10 +27,17 @@ public class YtThirdUserDTO extends BaseDTO {
26 27 @ApiModelProperty(value = "用户头像", required = false)
27 28 private String avatarUrl;
28 29
29   - @ApiModelProperty(value = "系统用户ID", required = true)
30   - @NotEmpty(message = "系统用户ID不能为空")
  30 + @ApiModelProperty(value = "系统用户ID", required = false)
31 31 private String appUserId;
32 32
  33 + @ApiModelProperty(value = "系统用户唯一标识,例如:用户ID、账号、手机号", required = true)
  34 + @NotEmpty(message = "系统用户唯一标识不能为空")
  35 + private String appUserKey;
  36 + @ApiModelProperty(value = "系统用户有效性验证密钥,例如:密码、短信验证码", required = true)
  37 + private String appUserSecret;
  38 + @ApiModelProperty(value = "有效性验证方式", required = true)
  39 + private LoginMethodEnum loginMethod;
  40 +
33 41
34 42
35 43 }
... ...
  1 +package org.thingsboard.server.common.data.yunteng.enums;
  2 +
  3 +/** 登录方式
  4 + * @author Administrator*/
  5 +public enum LoginMethodEnum {
  6 + ACCOUNT,
  7 + PHONE
  8 +}
... ...
... ... @@ -6,14 +6,30 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
6 6 import com.fasterxml.jackson.databind.JsonNode;
7 7 import lombok.RequiredArgsConstructor;
8 8 import lombok.extern.slf4j.Slf4j;
  9 +import org.springframework.beans.factory.annotation.Autowired;
  10 +import org.springframework.security.authentication.BadCredentialsException;
  11 +import org.springframework.security.authentication.InsufficientAuthenticationException;
  12 +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  13 +import org.springframework.security.core.userdetails.UsernameNotFoundException;
  14 +import org.springframework.security.crypto.password.PasswordEncoder;
9 15 import org.springframework.stereotype.Service;
10 16 import org.springframework.transaction.annotation.Transactional;
11 17 import org.thingsboard.server.common.data.StringUtils;
  18 +import org.thingsboard.server.common.data.audit.ActionType;
  19 +import org.thingsboard.server.common.data.id.EntityId;
  20 +import org.thingsboard.server.common.data.id.TenantId;
  21 +import org.thingsboard.server.common.data.id.UserId;
  22 +import org.thingsboard.server.common.data.security.Authority;
  23 +import org.thingsboard.server.common.data.yunteng.core.cache.CacheUtils;
12 24 import org.thingsboard.server.common.data.yunteng.core.exception.YtDataValidationException;
13 25 import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage;
14 26 import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO;
  27 +import org.thingsboard.server.common.data.yunteng.dto.UserDetailsDTO;
15 28 import org.thingsboard.server.common.data.yunteng.dto.YtOpinionDTO;
16 29 import org.thingsboard.server.common.data.yunteng.dto.YtThirdUserDTO;
  30 +import org.thingsboard.server.common.data.yunteng.dto.request.CodeTTL;
  31 +import org.thingsboard.server.common.data.yunteng.enums.MessageTypeEnum;
  32 +import org.thingsboard.server.common.data.yunteng.enums.MsgTemplatePurposeEnum;
17 33 import org.thingsboard.server.common.data.yunteng.enums.ThirdPlatformEnum;
18 34 import org.thingsboard.server.common.data.yunteng.utils.tools.YtPageData;
19 35 import org.thingsboard.server.dao.yunteng.entities.User;
... ... @@ -22,12 +38,15 @@ import org.thingsboard.server.dao.yunteng.entities.YtThirdUserEntity;
22 38 import org.thingsboard.server.dao.yunteng.mapper.UserMapper;
23 39 import org.thingsboard.server.dao.yunteng.mapper.YtOpinionMapper;
24 40 import org.thingsboard.server.dao.yunteng.mapper.YtThirdPlatformMapper;
25   -import org.thingsboard.server.dao.yunteng.service.AbstractBaseService;
26   -import org.thingsboard.server.dao.yunteng.service.UserOrganizationMappingService;
27   -import org.thingsboard.server.dao.yunteng.service.YtOpinionService;
28   -import org.thingsboard.server.dao.yunteng.service.YtThirdPlatformService;
  41 +import org.thingsboard.server.dao.yunteng.service.*;
29 42
30 43 import java.util.List;
  44 +import java.util.Objects;
  45 +import java.util.Optional;
  46 +import java.util.UUID;
  47 +
  48 +import static org.thingsboard.server.common.data.yunteng.constant.FastIotConstants.CacheConfigKey.MOBILE_LOGIN_SMS_CODE;
  49 +import static org.thingsboard.server.common.data.yunteng.constant.FastIotConstants.DEFAULT_DELIMITER;
31 50
32 51 @Slf4j
33 52 @Service
... ... @@ -36,7 +55,8 @@ public class YtThirdPlatformServiceImpl extends AbstractBaseService<YtThirdPlatf
36 55 implements YtThirdPlatformService {
37 56
38 57 private final UserMapper userMapper;
39   -
  58 + private final CacheUtils cacheUtils;
  59 + private final PasswordEncoder passwordEncoder;
40 60 @Override
41 61 public YtPageData<YtThirdUserDTO> pageDatas(IPage<YtThirdUserEntity> pageInfrom, ThirdPlatformEnum platformName, String name) {
42 62 Wrapper pageFilter = new QueryWrapper<YtThirdUserEntity>()
... ... @@ -53,25 +73,95 @@ public class YtThirdPlatformServiceImpl extends AbstractBaseService<YtThirdPlatf
53 73
54 74 @Override
55 75 @Transactional(rollbackFor=Exception.class)
56   - public YtThirdUserDTO saveOrUpdate(YtThirdUserDTO videoDTO) {
57   - User user = userMapper.selectById(videoDTO.getAppUserId());
  76 + public String saveOrUpdate(YtThirdUserDTO dto) {
  77 + User user = null;
  78 + switch (dto.getLoginMethod()){
  79 + case PHONE:
  80 + user = checkPhoneCode(dto.getAppUserKey(),dto.getAppUserSecret());
  81 + break;
  82 + case ACCOUNT:
  83 + user = checkUsernamePassword(dto.getAppUserKey(),dto.getAppUserSecret());
  84 + break;
  85 + }
58 86 if(user == null){
59 87 throw new YtDataValidationException(ErrorMessage.USER_NOT_EXISTS.getMessage());
60 88 }
61   - if(StringUtils.isEmpty(user.getAvatar()) && StringUtils.isNotEmpty(videoDTO.getAvatarUrl())){
62   - user.setAvatar(videoDTO.getAvatarUrl());
  89 + if(StringUtils.isEmpty(user.getAvatar()) && StringUtils.isNotEmpty(dto.getAvatarUrl())){
  90 + user.setAvatar(dto.getAvatarUrl());
63 91 userMapper.updateById(user);
64 92 }
65 93 Wrapper filter = new QueryWrapper<YtThirdUserEntity>().lambda()
66   - .eq(YtThirdUserEntity::getThirdUserId,videoDTO.getThirdUserId());
  94 + .eq(YtThirdUserEntity::getThirdUserId,dto.getThirdUserId());
67 95 YtThirdUserEntity oldVideo = baseMapper.selectOne(filter);
68 96 if (null == oldVideo) {
69   - baseMapper.insert(videoDTO.getEntity(YtThirdUserEntity.class));
  97 + baseMapper.insert(dto.getEntity(YtThirdUserEntity.class));
70 98 }else {
71   - videoDTO.setId(oldVideo.getId());
72   - baseMapper.updateById(videoDTO.getEntity(YtThirdUserEntity.class));
  99 + dto.setId(oldVideo.getId());
  100 + baseMapper.updateById(dto.getEntity(YtThirdUserEntity.class));
  101 + }
  102 + return user.getTbUser();
  103 + }
  104 +
  105 + /**
  106 + * 验证用户名和密码有效性
  107 + * @param key 用户名
  108 + * @param secret 密码
  109 + * @return
  110 + */
  111 + private User checkUsernamePassword(String key,String secret){
  112 + Wrapper filter = new QueryWrapper<User>()
  113 + .lambda()
  114 + .eq(User::getUsername,key);
  115 + User user = userMapper.selectOne(filter);
  116 +
  117 + if (user == null) {
  118 + throw new UsernameNotFoundException("User not found: " + key);
73 119 }
74   - return videoDTO;
  120 + if(!passwordEncoder.matches(secret, user.getPassword())){
  121 + throw new BadCredentialsException("Authentication Failed. Username or Password not valid.");
  122 + }
  123 + return user;
  124 + }
  125 +
  126 + /**
  127 + * 验证手机号和验证码有效性
  128 + * @param key 手机号
  129 + * @param secret 验证码
  130 + * @return
  131 + */
  132 + private User checkPhoneCode(String key,String secret){
  133 + Wrapper filter = new QueryWrapper<User>()
  134 + .lambda()
  135 + .eq(User::getPhoneNumber,key);
  136 + User users = userMapper.selectOne(filter);
  137 + if (users == null) {
  138 + throw new UsernameNotFoundException("phone number not found: " + key);
  139 + }
  140 + String cacheKey =
  141 + MsgTemplatePurposeEnum.FOR_LOGIN.name()
  142 + + DEFAULT_DELIMITER
  143 + + MessageTypeEnum.PHONE_MESSAGE.name()
  144 + + DEFAULT_DELIMITER
  145 + + key;
  146 +
  147 + boolean correct =
  148 + cacheUtils
  149 + .get(MOBILE_LOGIN_SMS_CODE, cacheKey)
  150 + .map(
  151 + o -> {
  152 + CodeTTL codeTTL = (CodeTTL) o;
  153 + if (System.currentTimeMillis() - codeTTL.getSendTs() < 5 * 60 * 1000) {
  154 + return Objects.equals(codeTTL.getCode(), secret);
  155 + } else {
  156 + return false;
  157 + }
  158 + })
  159 + .orElse(false);
  160 + Optional<UserDetailsDTO> optionalUser;
  161 + if (!correct) {
  162 + throw new BadCredentialsException("验证码不正确");
  163 + }
  164 + return users;
75 165 }
76 166
77 167
... ... @@ -84,6 +174,10 @@ public class YtThirdPlatformServiceImpl extends AbstractBaseService<YtThirdPlatf
84 174 return baseMapper.delete(filter) > 0;
85 175 }
86 176
  177 + @Override
  178 + public String login(String thirdUserId) {
  179 + return baseMapper.login(thirdUserId);
  180 + }
87 181
88 182 @Override
89 183 public String accessToken(String appKey, String appSecret) {
... ...
1 1 package org.thingsboard.server.dao.yunteng.mapper;
2 2
3 3 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  4 +import com.baomidou.mybatisplus.core.metadata.IPage;
4 5 import org.apache.ibatis.annotations.Mapper;
  6 +import org.apache.ibatis.annotations.Param;
  7 +import org.thingsboard.server.common.data.yunteng.dto.YtVideoDTO;
5 8 import org.thingsboard.server.dao.yunteng.entities.YtThirdUserEntity;
6 9
  10 +import java.util.List;
  11 +
7 12 /**
8 13 * @author Administrator
9 14 */
10 15 @Mapper
11 16 public interface YtThirdPlatformMapper extends BaseMapper<YtThirdUserEntity> {
  17 + /**
  18 + * 第三方登录接口
  19 + *
  20 + * @param thirdId
  21 + * @return
  22 + */
  23 + String login(@Param("thirdId") String thirdId);
12 24 }
... ...
... ... @@ -30,7 +30,7 @@ public interface YtThirdPlatformService extends BaseService<YtThirdUserEntity> {
30 30 * @param dto
31 31 * @return
32 32 */
33   - YtThirdUserDTO saveOrUpdate(YtThirdUserDTO dto);
  33 + String saveOrUpdate(YtThirdUserDTO dto);
34 34
35 35 /**
36 36 * @param deleteDTO
... ... @@ -38,6 +38,13 @@ public interface YtThirdPlatformService extends BaseService<YtThirdUserEntity> {
38 38 */
39 39 boolean deleteDataByIds(DeleteDTO deleteDTO);
40 40
  41 + /**
  42 + * 第三方登录
  43 + * @param thirdUserId
  44 + * @return
  45 + */
  46 + String login(String thirdUserId);
  47 +
41 48
42 49
43 50
... ...
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +
  4 +<mapper namespace="org.thingsboard.server.dao.yunteng.mapper.YtThirdPlatformMapper">
  5 +
  6 + <select id="login" resultType="string">
  7 + SELECT sus.tb_user
  8 + FROM iotfs_third_user base
  9 + LEFT JOIN sys_user sus ON base.app_user_id = sus.id
  10 + <where>
  11 + <if test="thirdId !=null and thirdId !=''">
  12 + AND base.third_user_id = #{thirdId}
  13 + </if>
  14 + </where>
  15 + </select>
  16 +
  17 +
  18 +</mapper>
... ...