mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-11-04 04:08:43 +08:00 
			
		
		
		
	完成 yudao-sso-demo-by-code 实现修改用户的信息
This commit is contained in:
		@@ -16,6 +16,8 @@ import java.nio.charset.StandardCharsets;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * OAuth 2.0 客户端
 | 
			
		||||
 *
 | 
			
		||||
 * 对应调用 OAuth2OpenController 接口
 | 
			
		||||
 */
 | 
			
		||||
@Component
 | 
			
		||||
public class OAuth2Client {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,8 +2,9 @@ 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.client.dto.user.UserUpdateReqDTO;
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.framework.core.SecurityUtils;
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
 | 
			
		||||
import org.springframework.core.ParameterizedTypeReference;
 | 
			
		||||
import org.springframework.http.*;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
@@ -13,7 +14,9 @@ import org.springframework.util.MultiValueMap;
 | 
			
		||||
import org.springframework.web.client.RestTemplate;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * OAuth 2.0 客户端
 | 
			
		||||
 * 用户 User 信息的客户端
 | 
			
		||||
 *
 | 
			
		||||
 * 对应调用 OAuth2UserController 接口
 | 
			
		||||
 */
 | 
			
		||||
@Component
 | 
			
		||||
public class UserClient {
 | 
			
		||||
@@ -42,6 +45,26 @@ public class UserClient {
 | 
			
		||||
        return exchange.getBody();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public CommonResult<Boolean> updateUser(UserUpdateReqDTO updateReqDTO) {
 | 
			
		||||
        // 1.1 构建请求头
 | 
			
		||||
        HttpHeaders headers = new HttpHeaders();
 | 
			
		||||
        headers.setContentType(MediaType.APPLICATION_JSON);
 | 
			
		||||
        headers.set("tenant-id", OAuth2Client.TENANT_ID.toString());
 | 
			
		||||
        addTokenHeader(headers);
 | 
			
		||||
        // 1.2 构建请求参数
 | 
			
		||||
        // 使用 updateReqDTO 即可
 | 
			
		||||
 | 
			
		||||
        // 2. 执行请求
 | 
			
		||||
        ResponseEntity<CommonResult<Boolean>> exchange = restTemplate.exchange(
 | 
			
		||||
                BASE_URL + "/update",
 | 
			
		||||
                HttpMethod.PUT,
 | 
			
		||||
                new HttpEntity<>(updateReqDTO, headers),
 | 
			
		||||
                new ParameterizedTypeReference<CommonResult<Boolean>>() {}); // 解决 CommonResult 的泛型丢失
 | 
			
		||||
        Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
 | 
			
		||||
        return exchange.getBody();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private static void addTokenHeader(HttpHeaders headers) {
 | 
			
		||||
        LoginUser loginUser = SecurityUtils.getLoginUser();
 | 
			
		||||
        Assert.notNull(loginUser, "登录用户不能为空");
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,35 @@
 | 
			
		||||
package cn.iocoder.yudao.ssodemo.client.dto.user;
 | 
			
		||||
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 更新用户基本信息 Request DTO
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public class UserUpdateReqDTO {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户昵称
 | 
			
		||||
     */
 | 
			
		||||
    private String nickname;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户邮箱
 | 
			
		||||
     */
 | 
			
		||||
    private String email;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 手机号码
 | 
			
		||||
     */
 | 
			
		||||
    private String mobile;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户性别
 | 
			
		||||
     */
 | 
			
		||||
    private Integer sex;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -3,9 +3,8 @@ 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 cn.iocoder.yudao.ssodemo.client.dto.user.UserUpdateReqDTO;
 | 
			
		||||
import org.springframework.web.bind.annotation.*;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
 | 
			
		||||
@@ -26,4 +25,16 @@ public class UserController {
 | 
			
		||||
        return userClient.getUser();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 更新当前登录用户的昵称
 | 
			
		||||
     *
 | 
			
		||||
     * @param nickname 昵称
 | 
			
		||||
     * @return 成功
 | 
			
		||||
     */
 | 
			
		||||
    @PutMapping("/update")
 | 
			
		||||
    public CommonResult<Boolean> updateUser(@RequestParam("nickname") String nickname) {
 | 
			
		||||
        UserUpdateReqDTO updateReqDTO = new UserUpdateReqDTO(nickname, null, null, null);
 | 
			
		||||
        return userClient.updateUser(updateReqDTO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,12 @@
 | 
			
		||||
package cn.iocoder.yudao.ssodemo.framework.config;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.framework.core.TokenAuthenticationFilter;
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.framework.core.filter.TokenAuthenticationFilter;
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.framework.core.handler.AccessDeniedHandlerImpl;
 | 
			
		||||
import org.springframework.context.annotation.Configuration;
 | 
			
		||||
import org.springframework.http.HttpMethod;
 | 
			
		||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 | 
			
		||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 | 
			
		||||
import org.springframework.security.web.AuthenticationEntryPoint;
 | 
			
		||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
@@ -12,17 +14,14 @@ import javax.annotation.Resource;
 | 
			
		||||
@Configuration
 | 
			
		||||
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
 | 
			
		||||
 | 
			
		||||
//    /**
 | 
			
		||||
//     * Token 认证过滤器 Bean
 | 
			
		||||
//     */
 | 
			
		||||
//    @Bean
 | 
			
		||||
//    public TokenAuthenticationFilter authenticationTokenFilter(OAuth2Client oauth2Client) {
 | 
			
		||||
//        return new TokenAuthenticationFilter(oauth2Client);
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private TokenAuthenticationFilter tokenAuthenticationFilter;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private AccessDeniedHandlerImpl accessDeniedHandler;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private AuthenticationEntryPoint authenticationEntryPoint;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void configure(HttpSecurity httpSecurity) throws Exception {
 | 
			
		||||
        // 设置 URL 安全权限
 | 
			
		||||
@@ -36,7 +35,12 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
 | 
			
		||||
                .and().authorizeRequests()
 | 
			
		||||
                .anyRequest().authenticated();
 | 
			
		||||
 | 
			
		||||
        // 设置处理器
 | 
			
		||||
        httpSecurity.exceptionHandling().accessDeniedHandler(accessDeniedHandler)
 | 
			
		||||
                .authenticationEntryPoint(authenticationEntryPoint);
 | 
			
		||||
 | 
			
		||||
        // 添加 Token Filter
 | 
			
		||||
        httpSecurity.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,10 @@
 | 
			
		||||
package cn.iocoder.yudao.ssodemo.framework.core;
 | 
			
		||||
package cn.iocoder.yudao.ssodemo.framework.core.filter;
 | 
			
		||||
 | 
			
		||||
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 cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.springframework.util.StringUtils;
 | 
			
		||||
import org.springframework.web.filter.OncePerRequestFilter;
 | 
			
		||||
@@ -0,0 +1,44 @@
 | 
			
		||||
package cn.iocoder.yudao.ssodemo.framework.core.handler;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.framework.core.util.ServletUtils;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.http.HttpStatus;
 | 
			
		||||
import org.springframework.security.access.AccessDeniedException;
 | 
			
		||||
import org.springframework.security.web.access.AccessDeniedHandler;
 | 
			
		||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
 | 
			
		||||
import javax.servlet.FilterChain;
 | 
			
		||||
import javax.servlet.ServletException;
 | 
			
		||||
import javax.servlet.http.HttpServletRequest;
 | 
			
		||||
import javax.servlet.http.HttpServletResponse;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 访问一个需要认证的 URL 资源,已经认证(登录)但是没有权限的情况下,返回 {@link GlobalErrorCodeConstants#FORBIDDEN} 错误码。
 | 
			
		||||
 *
 | 
			
		||||
 * 补充:Spring Security 通过 {@link ExceptionTranslationFilter#handleAccessDeniedException(HttpServletRequest, HttpServletResponse, FilterChain, AccessDeniedException)} 方法,调用当前类
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
@Component
 | 
			
		||||
@SuppressWarnings("JavadocReference")
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
 | 
			
		||||
            throws IOException, ServletException {
 | 
			
		||||
        // 打印 warn 的原因是,不定期合并 warn,看看有没恶意破坏
 | 
			
		||||
        log.warn("[commence][访问 URL({}) 时,用户({}) 权限不够]", request.getRequestURI(),
 | 
			
		||||
                SecurityUtils.getLoginUserId(), e);
 | 
			
		||||
        // 返回 403
 | 
			
		||||
        CommonResult<Object> result = new CommonResult<>();
 | 
			
		||||
        result.setCode(HttpStatus.FORBIDDEN.value());
 | 
			
		||||
        result.setMsg("没有该操作权限");
 | 
			
		||||
        ServletUtils.writeJSON(response, result);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,36 @@
 | 
			
		||||
package cn.iocoder.yudao.ssodemo.framework.core.handler;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.framework.core.util.ServletUtils;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.http.HttpStatus;
 | 
			
		||||
import org.springframework.security.core.AuthenticationException;
 | 
			
		||||
import org.springframework.security.web.AuthenticationEntryPoint;
 | 
			
		||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
 | 
			
		||||
import javax.servlet.FilterChain;
 | 
			
		||||
import javax.servlet.http.HttpServletRequest;
 | 
			
		||||
import javax.servlet.http.HttpServletResponse;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 访问一个需要认证的 URL 资源,但是此时自己尚未认证(登录)的情况下,返回 {@link GlobalErrorCodeConstants#UNAUTHORIZED} 错误码,从而使前端重定向到登录页
 | 
			
		||||
 *
 | 
			
		||||
 * 补充:Spring Security 通过 {@link ExceptionTranslationFilter#sendStartAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, AuthenticationException)} 方法,调用当前类
 | 
			
		||||
 */
 | 
			
		||||
@Component
 | 
			
		||||
@Slf4j
 | 
			
		||||
@SuppressWarnings("JavadocReference") // 忽略文档引用报错
 | 
			
		||||
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
 | 
			
		||||
        log.debug("[commence][访问 URL({}) 时,没有登录]", request.getRequestURI(), e);
 | 
			
		||||
        // 返回 401
 | 
			
		||||
        CommonResult<Object> result = new CommonResult<>();
 | 
			
		||||
        result.setCode(HttpStatus.UNAUTHORIZED.value());
 | 
			
		||||
        result.setMsg("账号未登录");
 | 
			
		||||
        ServletUtils.writeJSON(response, result);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
package cn.iocoder.yudao.ssodemo.framework.core;
 | 
			
		||||
package cn.iocoder.yudao.ssodemo.framework.core.util;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
 | 
			
		||||
import org.springframework.lang.Nullable;
 | 
			
		||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 | 
			
		||||
import org.springframework.security.core.Authentication;
 | 
			
		||||
@@ -0,0 +1,28 @@
 | 
			
		||||
package cn.iocoder.yudao.ssodemo.framework.core.util;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.extra.servlet.ServletUtil;
 | 
			
		||||
import cn.hutool.json.JSONUtil;
 | 
			
		||||
import org.springframework.http.MediaType;
 | 
			
		||||
 | 
			
		||||
import javax.servlet.http.HttpServletResponse;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 客户端工具类
 | 
			
		||||
 *
 | 
			
		||||
 * @author 芋道源码
 | 
			
		||||
 */
 | 
			
		||||
public class ServletUtils {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 返回 JSON 字符串
 | 
			
		||||
     *
 | 
			
		||||
     * @param response 响应
 | 
			
		||||
     * @param object 对象,会序列化成 JSON 字符串
 | 
			
		||||
     */
 | 
			
		||||
    @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码
 | 
			
		||||
    public static void writeJSON(HttpServletResponse response, Object object) {
 | 
			
		||||
        String content = JSONUtil.toJsonStr(object);
 | 
			
		||||
        ServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -20,6 +20,33 @@
 | 
			
		||||
				+ '&response_type=' + responseType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
		 * 修改昵称
 | 
			
		||||
     */
 | 
			
		||||
    function updateNickname() {
 | 
			
		||||
      const nickname = prompt("请输入新的昵称", "");
 | 
			
		||||
      if (!nickname) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      // 更新用户的昵称
 | 
			
		||||
      const accessToken = localStorage.getItem('ACCESS-TOKEN');
 | 
			
		||||
      $.ajax({
 | 
			
		||||
        url: "http://127.0.0.1:18080/user/update?nickname=" + nickname,
 | 
			
		||||
        method: 'PUT',
 | 
			
		||||
        headers: {
 | 
			
		||||
          'Authentication': 'Bearer ' + accessToken
 | 
			
		||||
        },
 | 
			
		||||
        success: function (result) {
 | 
			
		||||
          if (result.code !== 0) {
 | 
			
		||||
            alert('更新昵称失败,原因:' + result.msg)
 | 
			
		||||
            return;
 | 
			
		||||
          }
 | 
			
		||||
          alert('更新昵称成功!');
 | 
			
		||||
          $('#nicknameSpan').html(nickname);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
    $(function () {
 | 
			
		||||
      const accessToken = localStorage.getItem('ACCESS-TOKEN');
 | 
			
		||||
      // 情况一:未登录
 | 
			
		||||
@@ -58,7 +85,7 @@
 | 
			
		||||
	<!-- 情况二:已登录:1)展示用户信息;2)刷新访问令牌;3)退出登录 -->
 | 
			
		||||
	<div id="yesLoginDiv" style="display: none">
 | 
			
		||||
		您已登录!<button>退出登录</button> <br />
 | 
			
		||||
		昵称:<span id="nicknameSpan"> 加载中... </span> <button>修改昵称</button> <br />
 | 
			
		||||
		昵称:<span id="nicknameSpan"> 加载中... </span> <button onclick="updateNickname()">修改昵称</button> <br />
 | 
			
		||||
		访问令牌:<span id="accessTokenSpan"> 加载中... </span> <br />
 | 
			
		||||
	</div>
 | 
			
		||||
</body>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user