mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-11-04 12:18:42 +08:00 
			
		
		
		
	完成 yudao-sso-demo-by-code 实现获得用户信息
This commit is contained in:
		@@ -20,14 +20,14 @@ import java.nio.charset.StandardCharsets;
 | 
			
		||||
@Component
 | 
			
		||||
public class OAuth2Client {
 | 
			
		||||
 | 
			
		||||
    private static final String BASE_URL = "http://127.0.0.1:48080/admin-api/system/oauth2/";
 | 
			
		||||
    private static final String BASE_URL = "http://127.0.0.1:48080/admin-api/system/oauth2";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户编号
 | 
			
		||||
     *
 | 
			
		||||
     * 默认使用 1;如果使用别的租户,可以调整
 | 
			
		||||
     */
 | 
			
		||||
    private static final Long TENANT_ID = 1L;
 | 
			
		||||
    public static final Long TENANT_ID = 1L;
 | 
			
		||||
 | 
			
		||||
    private static final String CLIENT_ID = "yudao-sso-demo-by-code";
 | 
			
		||||
    private static final String CLIENT_SECRET = "test";
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,50 @@
 | 
			
		||||
package cn.iocoder.yudao.ssodemo.client;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.client.dto.user.UserInfoRespDTO;
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.framework.core.SecurityUtils;
 | 
			
		||||
import org.springframework.core.ParameterizedTypeReference;
 | 
			
		||||
import org.springframework.http.*;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
import org.springframework.util.LinkedMultiValueMap;
 | 
			
		||||
import org.springframework.util.MultiValueMap;
 | 
			
		||||
import org.springframework.web.client.RestTemplate;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * OAuth 2.0 客户端
 | 
			
		||||
 */
 | 
			
		||||
@Component
 | 
			
		||||
public class UserClient {
 | 
			
		||||
 | 
			
		||||
    private static final String BASE_URL = "http://127.0.0.1:48080/admin-api//system/oauth2/user";
 | 
			
		||||
 | 
			
		||||
    //    @Resource // 可优化,注册一个 RestTemplate Bean,然后注入
 | 
			
		||||
    private final RestTemplate restTemplate = new RestTemplate();
 | 
			
		||||
 | 
			
		||||
    public CommonResult<UserInfoRespDTO> getUser() {
 | 
			
		||||
        // 1.1 构建请求头
 | 
			
		||||
        HttpHeaders headers = new HttpHeaders();
 | 
			
		||||
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
 | 
			
		||||
        headers.set("tenant-id", OAuth2Client.TENANT_ID.toString());
 | 
			
		||||
        addTokenHeader(headers);
 | 
			
		||||
        // 1.2 构建请求参数
 | 
			
		||||
        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
 | 
			
		||||
 | 
			
		||||
        // 2. 执行请求
 | 
			
		||||
        ResponseEntity<CommonResult<UserInfoRespDTO>> exchange = restTemplate.exchange(
 | 
			
		||||
                BASE_URL + "/get",
 | 
			
		||||
                HttpMethod.GET,
 | 
			
		||||
                new HttpEntity<>(body, headers),
 | 
			
		||||
                new ParameterizedTypeReference<CommonResult<UserInfoRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
 | 
			
		||||
        Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
 | 
			
		||||
        return exchange.getBody();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void addTokenHeader(HttpHeaders headers) {
 | 
			
		||||
        LoginUser loginUser = SecurityUtils.getLoginUser();
 | 
			
		||||
        Assert.notNull(loginUser, "登录用户不能为空");
 | 
			
		||||
        headers.add("Authorization", "Bearer " + loginUser.getAccessToken());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,97 @@
 | 
			
		||||
package cn.iocoder.yudao.ssodemo.client.dto.user;
 | 
			
		||||
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 获得用户基本信息 Response dto
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public class UserInfoRespDTO {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户编号
 | 
			
		||||
     */
 | 
			
		||||
    private Long id;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户账号
 | 
			
		||||
     */
 | 
			
		||||
    private String username;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户昵称
 | 
			
		||||
     */
 | 
			
		||||
    private String nickname;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户邮箱
 | 
			
		||||
     */
 | 
			
		||||
    private String email;
 | 
			
		||||
    /**
 | 
			
		||||
     * 手机号码
 | 
			
		||||
     */
 | 
			
		||||
    private String mobile;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户性别
 | 
			
		||||
     */
 | 
			
		||||
    private Integer sex;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户头像
 | 
			
		||||
     */
 | 
			
		||||
    private String avatar;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 所在部门
 | 
			
		||||
     */
 | 
			
		||||
    private Dept dept;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 所属岗位数组
 | 
			
		||||
     */
 | 
			
		||||
    private List<Post> posts;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 部门
 | 
			
		||||
     */
 | 
			
		||||
    @Data
 | 
			
		||||
    public static class Dept {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 部门编号
 | 
			
		||||
         */
 | 
			
		||||
        private Long id;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 部门名称
 | 
			
		||||
         */
 | 
			
		||||
        private String name;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 岗位
 | 
			
		||||
     */
 | 
			
		||||
    @Data
 | 
			
		||||
    public static class Post {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 岗位编号
 | 
			
		||||
         */
 | 
			
		||||
        private Long id;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 岗位名称
 | 
			
		||||
         */
 | 
			
		||||
        private String name;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +1,29 @@
 | 
			
		||||
package cn.iocoder.yudao.ssodemo.controller;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.client.UserClient;
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.client.dto.user.UserInfoRespDTO;
 | 
			
		||||
import org.springframework.web.bind.annotation.GetMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
 | 
			
		||||
@RestController
 | 
			
		||||
@RequestMapping("/user")
 | 
			
		||||
public class UserController {
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private UserClient userClient;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获得当前登录用户的基本信息
 | 
			
		||||
     *
 | 
			
		||||
     * @return TODO
 | 
			
		||||
     * @return 用户信息;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段
 | 
			
		||||
     */
 | 
			
		||||
    @GetMapping("/get")
 | 
			
		||||
    public String getUser() {
 | 
			
		||||
        return "";
 | 
			
		||||
    public CommonResult<UserInfoRespDTO> getUser() {
 | 
			
		||||
        return userClient.getUser();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,4 +29,9 @@ public class LoginUser {
 | 
			
		||||
     */
 | 
			
		||||
    private List<String> scopes;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 访问令牌
 | 
			
		||||
     */
 | 
			
		||||
    private String accessToken;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,102 @@
 | 
			
		||||
package cn.iocoder.yudao.ssodemo.framework.core;
 | 
			
		||||
 | 
			
		||||
import org.springframework.lang.Nullable;
 | 
			
		||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 | 
			
		||||
import org.springframework.security.core.Authentication;
 | 
			
		||||
import org.springframework.security.core.context.SecurityContext;
 | 
			
		||||
import org.springframework.security.core.context.SecurityContextHolder;
 | 
			
		||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
 | 
			
		||||
import org.springframework.util.StringUtils;
 | 
			
		||||
 | 
			
		||||
import javax.servlet.http.HttpServletRequest;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 安全服务工具类
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
public class SecurityUtils {
 | 
			
		||||
 | 
			
		||||
    public static final String AUTHORIZATION_BEARER = "Bearer";
 | 
			
		||||
 | 
			
		||||
    private SecurityUtils() {}
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 从请求中,获得认证 Token
 | 
			
		||||
     *
 | 
			
		||||
     * @param request 请求
 | 
			
		||||
     * @param header 认证 Token 对应的 Header 名字
 | 
			
		||||
     * @return 认证 Token
 | 
			
		||||
     */
 | 
			
		||||
    public static String obtainAuthorization(HttpServletRequest request, String header) {
 | 
			
		||||
        String authorization = request.getHeader(header);
 | 
			
		||||
        if (!StringUtils.hasText(authorization)) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        int index = authorization.indexOf(AUTHORIZATION_BEARER + " ");
 | 
			
		||||
        if (index == -1) { // 未找到
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        return authorization.substring(index + 7).trim();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获得当前认证信息
 | 
			
		||||
     *
 | 
			
		||||
     * @return 认证信息
 | 
			
		||||
     */
 | 
			
		||||
    public static Authentication getAuthentication() {
 | 
			
		||||
        SecurityContext context = SecurityContextHolder.getContext();
 | 
			
		||||
        if (context == null) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        return context.getAuthentication();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前用户
 | 
			
		||||
     *
 | 
			
		||||
     * @return 当前用户
 | 
			
		||||
     */
 | 
			
		||||
    @Nullable
 | 
			
		||||
    public static LoginUser getLoginUser() {
 | 
			
		||||
        Authentication authentication = getAuthentication();
 | 
			
		||||
        if (authentication == null) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获得当前用户的编号,从上下文中
 | 
			
		||||
     *
 | 
			
		||||
     * @return 用户编号
 | 
			
		||||
     */
 | 
			
		||||
    @Nullable
 | 
			
		||||
    public static Long getLoginUserId() {
 | 
			
		||||
        LoginUser loginUser = getLoginUser();
 | 
			
		||||
        return loginUser != null ? loginUser.getId() : null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置当前用户
 | 
			
		||||
     *
 | 
			
		||||
     * @param loginUser 登录用户
 | 
			
		||||
     * @param request 请求
 | 
			
		||||
     */
 | 
			
		||||
    public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) {
 | 
			
		||||
        // 创建 Authentication,并设置到上下文
 | 
			
		||||
        Authentication authentication = buildAuthentication(loginUser, request);
 | 
			
		||||
        SecurityContextHolder.getContext().setAuthentication(authentication);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) {
 | 
			
		||||
        // 创建 UsernamePasswordAuthenticationToken 对象
 | 
			
		||||
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
 | 
			
		||||
                loginUser, null, Collections.emptyList());
 | 
			
		||||
        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
 | 
			
		||||
        return authenticationToken;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -3,10 +3,6 @@ package cn.iocoder.yudao.ssodemo.framework.core;
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.client.OAuth2Client;
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2CheckTokenRespDTO;
 | 
			
		||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 | 
			
		||||
import org.springframework.security.core.Authentication;
 | 
			
		||||
import org.springframework.security.core.context.SecurityContextHolder;
 | 
			
		||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.springframework.util.StringUtils;
 | 
			
		||||
import org.springframework.web.filter.OncePerRequestFilter;
 | 
			
		||||
@@ -17,7 +13,6 @@ import javax.servlet.ServletException;
 | 
			
		||||
import javax.servlet.http.HttpServletRequest;
 | 
			
		||||
import javax.servlet.http.HttpServletResponse;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Token 过滤器,验证 token 的有效性
 | 
			
		||||
@@ -35,13 +30,13 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
 | 
			
		||||
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
 | 
			
		||||
                                    FilterChain filterChain) throws ServletException, IOException {
 | 
			
		||||
        // 1. 获得访问令牌
 | 
			
		||||
        String token = obtainAuthorization(request);
 | 
			
		||||
        String token = SecurityUtils.obtainAuthorization(request, "Authentication");
 | 
			
		||||
        if (StringUtils.hasText(token)) {
 | 
			
		||||
            // 2. 基于 token 构建登录用户
 | 
			
		||||
            LoginUser loginUser = buildLoginUserByToken(token);
 | 
			
		||||
            // 3. 设置当前用户
 | 
			
		||||
            if (loginUser != null) {
 | 
			
		||||
                setLoginUser(loginUser, request);
 | 
			
		||||
                SecurityUtils.setLoginUser(loginUser, request);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -58,50 +53,12 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
 | 
			
		||||
            }
 | 
			
		||||
            // 构建登录用户
 | 
			
		||||
            return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
 | 
			
		||||
                    .setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes());
 | 
			
		||||
                    .setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes())
 | 
			
		||||
                    .setAccessToken(accessToken.getAccessToken());
 | 
			
		||||
        } catch (Exception exception) {
 | 
			
		||||
            // 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 从请求 Header 中,获得访问令牌
 | 
			
		||||
     *
 | 
			
		||||
     * @param request 请求
 | 
			
		||||
     * @return 访问令牌
 | 
			
		||||
     */
 | 
			
		||||
    private static String obtainAuthorization(HttpServletRequest request) {
 | 
			
		||||
        String authorization = request.getHeader("Authentication");
 | 
			
		||||
        if (!StringUtils.hasText(authorization)) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        int index = authorization.indexOf("Bearer ");
 | 
			
		||||
        if (index == -1) { // 未找到
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        return authorization.substring(index + 7).trim();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置当前用户
 | 
			
		||||
     *
 | 
			
		||||
     * @param loginUser 登录用户
 | 
			
		||||
     * @param request 请求
 | 
			
		||||
     */
 | 
			
		||||
    private static void setLoginUser(LoginUser loginUser, HttpServletRequest request) {
 | 
			
		||||
        // 创建 Authentication,并设置到上下文
 | 
			
		||||
        Authentication authentication = buildAuthentication(loginUser, request);
 | 
			
		||||
        SecurityContextHolder.getContext().setAuthentication(authentication);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) {
 | 
			
		||||
        // 创建 UsernamePasswordAuthenticationToken 对象
 | 
			
		||||
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
 | 
			
		||||
                loginUser, null, Collections.emptyList());
 | 
			
		||||
        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
 | 
			
		||||
        return authenticationToken;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@
 | 
			
		||||
            alert('获得个人信息失败,原因:' + result.msg)
 | 
			
		||||
            return;
 | 
			
		||||
          }
 | 
			
		||||
          $('nicknameSpan').html(result.data.nickname);
 | 
			
		||||
          $('#nicknameSpan').html(result.data.nickname);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    })
 | 
			
		||||
@@ -57,8 +57,8 @@
 | 
			
		||||
 | 
			
		||||
	<!-- 情况二:已登录:1)展示用户信息;2)刷新访问令牌;3)退出登录 -->
 | 
			
		||||
	<div id="yesLoginDiv" style="display: none">
 | 
			
		||||
		您已登录!点击 <a href="#" onclick="ssoLogin()">退出 </a> 系统 <br />
 | 
			
		||||
		昵称:<span id="nicknameSpan"> 加载中... </span> <br />
 | 
			
		||||
		您已登录!<button>退出登录</button> <br />
 | 
			
		||||
		昵称:<span id="nicknameSpan"> 加载中... </span> <button>修改昵称</button> <br />
 | 
			
		||||
		访问令牌:<span id="accessTokenSpan"> 加载中... </span> <br />
 | 
			
		||||
	</div>
 | 
			
		||||
</body>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user