1.前言

最近项目上需要实现一个统一认证服务器,自然而然的就想到了目前最流行的授权解决方案OAuth2.0;客户端主要通过授权码模式、密码模式、客户端凭证模式、隐式授权模式四种方式获取临时的令牌,最后通过令牌获得访问用户资源的权限。关于OAuth2.0的资料很多,这边就不细讲oauth2.0的内容了。
如果是刚接触OAuth2.0的朋友,在刚开始的时候可能会有一些很难理解或者很难想通的地方,这很正常,但是需要多查看资料、多思考,最后把四种模式的流程走一遍,你就会慢慢的理解了。
参考资料:理解OAuth2.0、OAuth 2.0的一种解释

本文主要实现了OAuth2.0的统一认证、鉴权功能,但在最基础的功能上做了些改进:
1.自定义的授权码code实现
2.自定义的授权令牌token实现(JWT)
3.授权码code、token等信息使用redis存储,并使用fastjson序列化
4.扩展ClientDetails,添加trusted属性,对于受信任的client跳过用户授权操作
5.不同的资源访问权限配置
6.统一OAuth接口返回格式(包含异常处理)
7.自定义登录界面实现(包含验证码)
8.仿微信用户授权页面实现

2.正文

2.1 数据库

首先我们来说一下关于OAuth2.0的相关的数据库表,OAuth2.0 相关表结构说明;
上面链接中的表结构是spring-security-oauth2提供的默认字段,但是实际项目中我们可能不止包含这些字段,例如oauth_client_details中可能包含接入应用的名称、图片信息等。
Spring boot 2.x集成oauth2.0、redis、mybatis实现统一认证相关的数据库表结构
上图是本文所需要使用的数据库表结构,其中code、token等信息直接会存至redis中。

2.2 pom
	<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><!-- thymeleaf --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!--thymeleaf对security5的支持依赖--><dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity5</artifactId></dependency><!-- aop --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- spring security --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope></dependency><!-- jdbc --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!-- mybatis-plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.3.1.tmp</version></dependency><!-- alibaba的druid数据库连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.18</version></dependency><!-- json --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.60</version></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version><scope>runtime</scope></dependency><!-- Redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><!-- spring session --><dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId></dependency><!-- oauth2.0 --><dependency><groupId>org.springframework.security.oauth</groupId><artifactId>spring-security-oauth2</artifactId><version>2.3.4.RELEASE</version></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-taglibs</artifactId><version>4.2.3.RELEASE</version><exclusions><exclusion><groupId>org.springframework.security</groupId><artifactId>spring-security-acl</artifactId></exclusion><exclusion><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId></exclusion><exclusion><groupId>org.springframework</groupId><artifactId>spring-core</artifactId></exclusion><exclusion><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId></exclusion></exclusions></dependency></dependencies>
2.3 核心代码

由于代码量比较多,本文只贴出核心代码,更多代码请查看源码。

2.3.1 Spring Security配置类
@Slf4j
@Configuration
@EnableWebSecurity
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {@Autowiredprivate OauthUserDetailService userDetailService;@Autowiredprivate ValidateCodeFilter validateCodeFilter;@Autowiredprivate SecurityLoginFailureHandler securityLoginFailureHandler;/*** 引入OAuth 2.0必须要暴露的bean* @return* @throws Exception*/@Override@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Overridepublic void configure(WebSecurity web) throws Exception {//Ignore, publicweb.ignoring().antMatchers("/public/**", "/static/**");}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().ignoringAntMatchers("/oauth/authorize", "/oauth/token", "/oauth/rest_token");http.authorizeRequests().antMatchers("/code/**").permitAll().antMatchers("/antd/**", "/vue/**", "/img/**").permitAll().antMatchers("/oauth/rest_token*").permitAll().antMatchers("/doLogin").permitAll().antMatchers("/login*").permitAll().antMatchers(HttpMethod.GET, "/login*").anonymous().anyRequest().authenticated().and().formLogin().loginPage("/login").loginProcessingUrl("/doLogin").defaultSuccessUrl("/index")// 登录失败异常处理.failureHandler(securityLoginFailureHandler)//.failureUrl("/login?error=1").usernameParameter("username").passwordParameter("password").and().logout().logoutUrl("/logout").deleteCookies("JSESSIONID").logoutSuccessUrl("/login").and().exceptionHandling().and()//验证码过滤器.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);http.authenticationProvider(authenticationProvider());}@Beanpublic AuthenticationProvider authenticationProvider() {DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();daoAuthenticationProvider.setUserDetailsService(userDetailService);daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());return daoAuthenticationProvider;}/*** BCrypt  加密** @return PasswordEncoder*/@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
2.3.2 OAuth 2.0授权服务器配置
/*** oauth2.0授权服务器配置*/@Configuration@EnableAuthorizationServerprotected class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {@Autowiredprivate TokenStore tokenStore;@Autowiredprivate OauthClientDetailsService oauthClientDetailsService;@Autowiredprivate OauthCodeService authorizationCodeServices;@Autowiredprivate OauthUserDetailService userDetailService;@Autowired@Qualifier("authenticationManagerBean")private AuthenticationManager authenticationManager;@Autowiredprivate OauthWebResponseExceptionTranslator oauthWebResponseExceptionTranslator;@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.withClientDetails(oauthClientDetailsService);}/*** token 存储处理类,使用redis* @param connectionFactory* @return*/@Beanpublic TokenStore tokenStore(RedisConnectionFactory connectionFactory) {final RedisTokenStore redisTokenStore = new RedisTokenStore(connectionFactory);// 前缀redisTokenStore.setPrefix("TOKEN:");// 序列化策略,使用fastjsonredisTokenStore.setSerializationStrategy(new FastJsonRedisTokenStoreSerializationStrategy());return redisTokenStore;}/*** 认证端点配置* @param endpoints* @throws Exception*/@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.tokenStore(tokenStore).allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)// 自定义认证异常处理.exceptionTranslator(oauthWebResponseExceptionTranslator)// 自定义的授权码模式的code(授权码)处理,使用redis存储.authorizationCodeServices(authorizationCodeServices)// 用户信息service.userDetailsService(userDetailService)// 用户授权确认处理器.userApprovalHandler(userApprovalHandler())// 注入authenticationManager来支持password模式.authenticationManager(authenticationManager)// 自定义授权确认页面.pathMapping("/oauth/confirm_access", "/approval");}/*** AuthorizationServer的端点(/oauth/**)安全配置(访问规则、过滤器、返回结果处理等)* @param oauthServer* @throws Exception*/@Overridepublic void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {// 允许 /oauth/token的端点表单认证oauthServer.allowFormAuthenticationForClients().tokenKeyAccess("permitAll()")// 允许 /oauth/token_check端点的访问.checkTokenAccess("permitAll()");}@Beanpublic OAuth2RequestFactory oAuth2RequestFactory() {return new DefaultOAuth2RequestFactory(oauthClientDetailsService);}@Beanpublic UserApprovalHandler userApprovalHandler() {OauthUserApprovalHandler userApprovalHandler = new OauthUserApprovalHandler();userApprovalHandler.setTokenStore(tokenStore);userApprovalHandler.setClientDetailsService(oauthClientDetailsService);userApprovalHandler.setRequestFactory(oAuth2RequestFactory());return userApprovalHandler;}}
2.3.3 资源服务器配置

资源服务器配置可以配置多个,主要是根据不同的权限可以访问不同的资源。

/*** 资源服务器配置*/@Configuration@EnableResourceServerprotected class ApiResourceServerConfiguration extends ResourceServerConfigurerAdapter {@Autowiredprivate OauthTokenExtractor oauthTokenExtractor;@Autowiredprivate OauthExceptionEntryPoint oauthExceptionEntryPoint;@Autowiredprivate OauthAccessDeniedHandler oauthAccessDeniedHandler;@Overridepublic void configure(ResourceServerSecurityConfigurer resources) {resources.resourceId(RESOURCE_ID).stateless(false);// token提取器resources.tokenExtractor(oauthTokenExtractor)// token异常处理器.authenticationEntryPoint(oauthExceptionEntryPoint)// 无权限异常处理器.accessDeniedHandler(oauthAccessDeniedHandler);}@Overridepublic void configure(HttpSecurity http) throws Exception {http// STATELESS表示一定要携带access_token才能访问,无法通过session访问.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().requestMatchers().antMatchers("/get/**").and().authorizeRequests().antMatchers("/get/**").access("#oauth2.hasScope('read')");}}
2.3.4 ClientDetails的service类
@Slf4j
@Service("oauthClientDetailsService")
public class OauthClientDetailsService extends ServiceImpl<OauthClientDetailsMapper, OauthClientDetails> implements ClientDetailsService {@Resourceprivate RedisTemplate<String, OauthClientDetails> redisTemplate;private String prefix = "ClientDetails:";@Overridepublic ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {// 优先从redis缓存中获取,不存在则从数据库中获取OauthClientDetails oauthClientDetails = redisTemplate.opsForValue().get(prefix + clientId);if(oauthClientDetails == null){LambdaQueryWrapper<OauthClientDetails> query = new LambdaQueryWrapper<>();query.eq(OauthClientDetails::getAppKey, clientId);oauthClientDetails = super.getOne(query);if(oauthClientDetails == null){return null;}redisTemplate.opsForValue().set(prefix + clientId, oauthClientDetails, 1, TimeUnit.HOURS);}return new ClientDetailsAdapter(oauthClientDetails);}

由于数据库表返回的类是OAuthClientDetails,因此需要一个适配器类用来兼容该实体类

public class ClientDetailsAdapter implements ClientDetails {private OauthClientDetails clientDetails;public ClientDetailsAdapter(OauthClientDetails clientDetails) {this.clientDetails = clientDetails;}@Overridepublic String getClientId() {return clientDetails.getAppKey();}@Overridepublic Set<String> getResourceIds() {return CollectionUtil.getSetBySplit(clientDetails.getResourceIds());}@Overridepublic boolean isSecretRequired() {return true;}@Overridepublic String getClientSecret() {return clientDetails.getAppSecret();}/*** 客户端是否为特定范围,如果该值返回false,则忽略身份认证的请求范围(scope的值)* @return*/@Overridepublic boolean isScoped() {return true;}/*** 客户端拥有的授权范围* @return*/@Overridepublic Set<String> getScope() {return CollectionUtil.getSetBySplit(clientDetails.getScope());}/*** 客户端拥有的授权方式* @return*/@Overridepublic Set<String> getAuthorizedGrantTypes() {return CollectionUtil.getSetBySplit(clientDetails.getAuthorizedGrantTypes());}@Overridepublic Set<String> getRegisteredRedirectUri() {return CollectionUtil.getSetBySplit(clientDetails.getRedirectUri());}@Overridepublic Collection<GrantedAuthority> getAuthorities() {List<GrantedAuthority> list = new ArrayList<>();for (String item : CollectionUtil.getSetBySplit(clientDetails.getAuthorities())) {GrantedAuthority authority = new SimpleGrantedAuthority(item);list.add(authority);}return list;}@Overridepublic Integer getAccessTokenValiditySeconds() {return clientDetails.getAccessTokenValidity();}@Overridepublic Integer getRefreshTokenValiditySeconds() {return clientDetails.getRefreshTokenValidity();}@Overridepublic boolean isAutoApprove(String scope) {return false;}@Overridepublic Map<String, Object> getAdditionalInformation() {return null;}/*** 第三应用是否可信任* @return*/public boolean isTrusted(){return clientDetails.getTrusted().intValue() == 1;}public OauthClientDetails getClientDetails() {return clientDetails;}
2.3.5 UserDetails的service类
@Service
public class OauthUserDetailService extends ServiceImpl<OauthUserMapper, OauthUser> implements UserDetailsService, Serializable {private static final long serialVersionUID = 1170885289644276974L;@Resourceprivate OauthUserMapper mapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {OauthUser user = mapper.getUserByAccount(username);if(user == null){throw new UsernameNotFoundException("该用户不存在");}return new UserDetailsAdapter(user);}/*** 获取用户受保护的信息* @param account* @return*/public JSONObject getProtectedUserInfo(String account){OauthUser user = mapper.getUserByAccount(account);if(user != null){JSONObject result = new JSONObject();result.put("id", user.getId());result.put("userName", user.getUserName());result.put("phone", user.getPhone());result.put("gender", user.getGender());if(!CollectionUtils.isEmpty(user.getRoleList())){result.put("role", user.getRoleList().stream().map(OauthRole::getRoleCode).collect(Collectors.toList()));}return result;}return null;}/*** 修改用户姓名* @param account* @param userName* @return*/public boolean updateUserName(String account, String userName){OauthUser user = mapper.getUserByAccount(account);if(user != null){user.setUserName(userName);user.setUpdateTime(LocalDateTime.now());mapper.updateById(user);return true;}return false;}
}

同样,这边也用到了适配器模式

@Setter
public class UserDetailsAdapter implements UserDetails {// fastJson反序列化的时候需要有属性去接受redis中的属性值private String username;private String password;/*** 权限*/private List<GrantedAuthority> authorities;private boolean enable;/*** 角色的默认前缀* @see {@link org.springframework.security.access.expression.SecurityExpressionRoot#setDefaultRolePrefix}*/private static final String defaultRolePrefix = "ROLE_";// 由于使用了FastJson进行序列化和反序列化,因此必须要有一个空的构造器public UserDetailsAdapter(){}public UserDetailsAdapter(OauthUser oauthUser) {this.username = oauthUser.getAccount();this.password = oauthUser.getPassword();List<GrantedAuthority> list = new ArrayList<>();if(!StringUtils.isEmpty(oauthUser.getRoleList())){for (OauthRole role : oauthUser.getRoleList()) {GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(defaultRolePrefix + role.getRoleCode());list.add(grantedAuthority);}}this.authorities = list;this.enable = (oauthUser.getDelFlag() == 0 && oauthUser.getStatus() == 0);}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return authorities;}@Overridepublic String getPassword() {return password;}@Overridepublic String getUsername() {return username;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return enable;}
2.3.6 统一返回数据模型

定义一个Result类,用于接口返回

@Data
public class Result implements Serializable {private static final long serialVersionUID = 1L;/*** 成功标志*/private boolean success = true;/*** 返回处理消息*/private String msg = "ok";/*** 返回代码*/private Integer code = 0;/*** 返回数据对象 data*/private Object data;public Result() {}public Result(int code, String msg, Object data) {this.code = code;this.msg = msg;this.data = data;}public Result(ResultStatusCode resultStatusCode, Object data) {this(resultStatusCode.getCode(), resultStatusCode.getMsg(), data);}public Result(int code, String msg) {this(code, msg, null);}public Result(ResultStatusCode resultStatusCode) {this(resultStatusCode, null);}public boolean isSuccess() {return this.code == 0;}public static Result ok() {return new Result(ResultStatusCode.OK);}public static Result ok(Object obj) {return new Result(ResultStatusCode.OK, obj);}public static Result error(ResultStatusCode resultStatusCode){return new Result(resultStatusCode);}public static Result error(ResultStatusCode resultStatusCode, Object obj){return new Result(resultStatusCode, obj);}public static Result error(String msg){return new Result(5000, msg, null);}
}

错误码定义类

public enum ResultStatusCode {OK(0, "OK"),BAD_REQUEST(400, "参数解析失败"),INVALID_TOKEN(401, "无效的Access-Token"),METHOD_NOT_ALLOWED(405, "不支持当前请求方法"),SYSTEM_ERR(500, "服务器运行异常"),PERMISSION_DENIED(10001, "权限不足"),TOKEN_MISS(10002, "Token缺失");private int code;private String msg;public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}private ResultStatusCode(int code, String msg) {this.code = code;this.msg = msg;}
}
2.3.7 OAuth2.0 接口返回结果处理

在AuthorizationServerConfiguration和ApiResourceServerConfiguration类中,我们配置许多自定义异常处理类,主要是用来规范接口输出,客户端接入时可以直接通过返回的错误码来定义问题。
无权限异常处理

@Component
public class OauthAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) {accessDeniedException.printStackTrace();response.setStatus(HttpStatus.OK.value());ResultUtil.writeJavaScript(response, Result.error(ResultStatusCode.PERMISSION_DENIED));}
}

token异常处理

@Component
public class OauthExceptionEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException){authException.printStackTrace();Throwable cause = authException.getCause();response.setStatus(HttpStatus.OK.value());if(cause instanceof InvalidTokenException) {ResultUtil.writeJavaScript(response, Result.error(ResultStatusCode.INVALID_TOKEN));}else{ResultUtil.writeJavaScript(response, Result.error(ResultStatusCode.TOKEN_MISS));}}
}

成功返回结果处理

@ControllerAdvice(basePackages = "org.springframework.security.oauth2.provider.endpoint")
public class ResponseAdvice implements ResponseBodyAdvice {@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {// 只对oauth提供的相关接口返回的Json数据进行处理if(request.getURI().getPath().startsWith("/oauth") && selectedContentType.includes(MediaType.APPLICATION_JSON)){// 排除异常处理中已经处理过的json结果if(!JSON.toJSONString(body).contains("code")){return Result.ok(body);}}return body;}
}
2.4 自定义登录页面(thymeleaf、vue、ant design vue)

由于Spring-Security默认的登录页面比较单调,并且没有实现验证码功能,所以需要自己去实现登录页面,在WebSecurityConfigurer配置类中已经指定了登录界面url为/login,对应html如下

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>统一认证登录页面</title><link th:href="@{/antd/dist/antd.css}" rel="stylesheet"><script th:src="@{/vue/vue.min.js}"></script><script th:src="@{/antd/dist/antd.min.js}"></script><script th:src="@{/vue/moment.min.js}"></script><script th:src="@{/vue/jquery.min.js}"></script><style type="text/css">.main {background-color: #2b4b6b;height: 100%;width: 100%;}.login_box {width: 450px;background-color: #fff;border-radius: 3px;position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);}.login_box .avator_box {height: 130px;width: 130px;border: 1px solid #eee;border-radius: 50%;padding: 10px;box-shadow: 0 0 10px #ddd;position: absolute;left: 50%;transform: translate(-50%, -50%);background-color: #fff;}.login_box .avator_box img {width: 100%;height: 100%;border-radius: 50%;background-color: #eee;}.login_box .login_form {width: 100%;margin-top: 20%;padding: 0 10%;}</style>
</head>
<body><div id="app" style="width: 100vw; height: 100vh;"><div class="main"><div class="login_box"><div class="avator_box"><img th:src="@{/img/logo.png}" /></div><div class="login_form"><form id="userForm" th:action="@{/doLogin}" method="post" style="display: none;"><input name="username" :value="form.username"><input name="password" type="password" :value="form.password"><input name="code" :value="form.code"><input name="timestamp" :value="currdatetime"></form><a-form-model ref="loginForm" :model="form" :rules="rules" @keyup.enter.native="handleSubmit"><a-form-model-item ref="username" prop="username"><a-input v-model="form.username" size="large" placeholder="请输入登录账号"><a-iconslot="prefix"type="user":style="{ color: 'rgba(0,0,0,.25)' }"/></a-input></a-form-model-item><a-form-model-item ref="password" prop="password"><a-input v-model="form.password" type="password" size="large" placeholder="请输入密码"><a-iconslot="prefix"type="lock":style="{ color: 'rgba(0,0,0,.25)' }"/></a-input></a-form-model-item><a-form-model-item ref="code" prop="code"><a-row><a-col :span="16"><a-input v-model="form.code" size="large" placeholder="请输入验证码"></a-input></a-col><a-col :span="8" style="text-align: right;"><img v-if="requestCodeSuccess" :src="randCodeImage" @click="handleChangeCheckCode"/><img v-else th:src="@{/img/checkcode.png}" @click="handleChangeCheckCode"/></a-col></a-row></a-form-model-item><a-form-model-item><a-button type="primary" @click="handleSubmit" size="large" style="width: 100%;">登录</a-button></a-form-model-item></a-form-model></div></div></div></div><script type="text/javascript">var vue = new Vue({el: '#app',data: {form: {username: '',password: '',code: ''},rules: {username: [{required: true, message: '请输入登录账号'}],password: [{required: true, message: '请输入密码'}],code: [{required: true, message: '请输入验证码'}]},currdatetime: '',requestCodeSuccess: false,randCodeImage: ''},created: function(){this.handleChangeCheckCode()var error = this.getUrlParam('error');if(error){if(error == 'codeError'){this.$notification.error({message: '登录失败',description: '验证码错误'})}else{this.$notification.error({message: '登录失败',description: '账号或者密码错误'})}}},methods: {handleChangeCheckCode: function() {this.currdatetime = new Date().getTime();var that = this$.get('/code/' + this.currdatetime, function(res){if(res.success){that.randCodeImage = res.datathat.requestCodeSuccess=true}else{that.$message.error(res.message)that.requestCodeSuccess=false}},'json')},handleSubmit: function(){this.$refs.loginForm.validate(function(valid){if(valid){$('#userForm').submit()}})},getUrlParam: function (paraName) {var url = document.location.toString();var arrObj = url.split("?");if (arrObj.length > 1) {var arrPara = arrObj[1].split("&");var arr;for (var i = 0; i < arrPara.length; i++) {arr = arrPara[i].split("=");if (arr != null && arr[0] == paraName) {return arr[1];}}return "";} else {return "";}}}})</script>
</body>
</html>

其中验证码需要通过过滤器处理,在WebSecurityConfigurer配置类中添加自定义过滤器

@Component
public class ValidateCodeFilter extends OncePerRequestFilter {@Autowiredprivate SecurityLoginFailureHandler securityLoginFailureHandler;@Resourceprivate RedisTemplate<String, String> redisTemplate;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {if(request.getRequestURI().equals("/doLogin") && request.getMethod().equalsIgnoreCase(HttpMethod.POST.name())){try {validate(request);}catch (ValidateCodeException e){securityLoginFailureHandler.onAuthenticationFailure(request, response, e);return;}}// 通过的情况下,继续执行其他过滤器链filterChain.doFilter(request, response);}private void validate(HttpServletRequest request) throws ServletRequestBindingException {String code = ServletRequestUtils.getStringParameter(request, "code");String timestamp = ServletRequestUtils.getStringParameter(request, "timestamp");String realKey = MD5Util.MD5Encode(code.toLowerCase() + timestamp, "utf-8");String serverCode = redisTemplate.opsForValue().get(realKey);redisTemplate.delete(realKey);if(serverCode == null || !serverCode.equalsIgnoreCase(code)){throw new ValidateCodeException("验证码不正确");}}
}

3 测试

在测试之前我们需要有用户信息和三方应用信息数据,博主为了偷懒没有写单独的维护页面,直接在测试类(SpringBootSecurityOauthApplicationTests)中单独写了createUserAndRole()和createClientDetails()方法,修改其中的参数直接运行即可;不过需要注意的是OauthUser的密码和OauthClientDetails的应用密钥都是采用的BCrypt加密,因此需要提前保存

登录页面效果如下:
登录页面-验证码错误
登录页面-用户名、密码错误
登录成功
OAuth2.0授权码方式测试:
授权码模式-1
授权码模式-2
授权码模式-3
授权码模式-4
用户资源访问测试如下:
获取用户信息
修改用户姓名
异常情况测试:
异常-token miss
异常-token invalid
异常-permission denied

4.结尾

本文只包含OAuth2.0认证服务器端和资源服务器端,不包含OAuth2.0的客户端,客户端需要自己去实现。

完整源码地址

参考
1.https://www.oauth.com/
2.https://blog.csdn.net/monkeyking1987/article/details/16828059
3.https://projects.spring.io/spring-security-oauth/docs/oauth2.html

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. [Debug] 法语输入

    Antimony已经想学法语很久了 为了不让父母多嘴(因为我爸比我妈还话痨) Antimony决定自学法语 为了不留痕迹,Antimony决定使用电脑学习法语,不留下笔记(免得老两口说我不务正业/提供唠叨) 因此Antimony遇到了一个困难:如何输入法文字符? 按照特殊符号输入的效率极低 作为…...

    2024/4/11 19:35:39
  2. 工业机器人的类型以及应用方式

    机器人按其用途分 ①工业机器人它用于焊接、喷漆、组装、搬运、密封等作业。 ②医疗福利机器人它用于护理、帮助病员和残疾人。 ③教育、电子游戏机器人它用于教育、研究、电子游戏等。 ④特殊工作机器人它用于擦窗、建筑、宇宙开发、海洋开发、原子能发电站的维护和检修等。 ③…...

    2024/4/24 21:59:53
  3. Linux Kubernetes Service 之 Ingress

    官方文档:https://kubernetes.io/zh/docs/concepts/services-networking/ingress/ 一、Ingress介绍 一种全局的、为了代理不同后端 Service 而设置的负载均衡服务,就是 Kubernetes 里的Ingress 服务。 Ingress由两部分组成:Ingress controller和Ingress服务。 Ingress Contr…...

    2024/5/2 14:35:08
  4. C# 多线程编程三大核心基础特点winform例子

    /*总结,多线程和普通编程最大区别:* 第一个:同步线程卡界面,* 关闭界面,点击界面都是由主线程完成,当进行计算时主线程无暇处理点击,拖动界面等* * 第二个:同步线程比较慢,执行五次10亿次计算,花费将近12秒,只有一个线程* 异步线程比较快,花费5秒时间,一共五个线程…...

    2024/4/28 22:14:00
  5. 笔记:JAVA_多态

    笔记:JAVA_多态一、多态的概述二、多态的格式三、 多态的变量调用和方法调用(以“二”中的多态格式为例)1、变量的调用2、方法的调用四、对象的向上转型五、对象的向下转型1、概述2、格式3、注意4、引出问题六、instanceof关键字七、注意事项 一、多态的概述多态是继封装、继…...

    2024/4/25 11:12:14
  6. VMware虚拟机磁盘文件vmdk单文件转多文件相互转换

    VMware虚拟机磁盘文件vmdk单文件转多文件相互转换设置环境变量编辑新建一个,把vmware的路径加进去,比如我的路径是C:\Users\Administrator>vmware-vdiskmanager.exeVMware Virtual Disk Manager - build 6661328.Usage: vmware-vdiskmanager.exe OPTIONS <disk-name>…...

    2024/5/6 15:36:13
  7. 干活的累死累活,到头来干不过写PPT的,那就让我们做好PPT

    前言2019年年初,新东方年会的一个视频火爆全网,里面说出了无数职场人士的心声:干活的累死累活,到头来干不过写PPT的!也有网友表示:写好PPT和做好PPT在职场上就是一种能力,一份好的PPT是内容好加视觉美观。在平时的科研过程中,我们经常会输出一些二维的平面图,二维平面图…...

    2024/4/10 12:03:30
  8. 二季度电商业务增幅97%,沃尔玛正面“叫板”亚马逊?

    “100块钱3桶油,最后一天甩货了!” 8月17日下午4点,北京市海淀区知春路的沃尔玛大卖场正式关闭,结局了15年的营业时光。关店前两三天,抢购特价商品、退购物卡的人头攒动。 据美股研究社了解,今年以来沃尔玛已在中国关闭了多家门店:4月14日,沃尔玛南京秦淮店闭店;5月14…...

    2024/4/10 12:03:29
  9. Snort预处理器之`AppId Preprocessor`

    文章目录1 AppId 预处理器简介2 AppId 预处理的依赖3 AppId 预处理器的配置4 AppId 预处理器的规则选项5 应用程序规则事件6 应用程序使用情况统计7 开放式检测器软件包(ODP)安装8 用户创建的应用程序检测器 1 AppId 预处理器简介 随着网络的复杂性和网络流量的增长,网络管理…...

    2024/5/2 20:04:09
  10. kafka教程

    kafka教程第1章 Kafka概述1.1 定义1.2 消息队列1.2.1 传统消息队列的应用场景消息队列的好处1.2.2 消息队列的两种模式1.3 什么是Kafka1.4 Kafka架构1.5 kafka名词解释1.6 消息格式第2章 Kafka集群部署2.1 环境准备2.1.1 集群规划2.1.2 jar包下载2.2 Kafka集群部署2.3 Kafka命令…...

    2024/4/25 0:30:52
  11. 看了保证让你高呼“牛逼”的RocketMQ知识体系

    前言:如果你现在正在看这句话,那么你需要有点时间来对这篇文章进行阅读。文章篇幅超 超超超超长,跟我一样!,文末也整合了一些RocketMQ的资料! 谢谢!如果有帮助,麻烦点赞支持我!Windows安装部署 下载 地址:[https://www.apache.org/dyn/closer.cgi?path=rocketmq/4.5…...

    2024/4/26 3:08:28
  12. 用最形象易懂的方式解释什么是网络漏洞

    什么是漏洞?举一个流行的例子,上帝为你关上了门,却忘了关上窗户。您从窗户钻了进来,偷看了上帝的老婆的泡澡。那个没关的窗户就是上帝粗心留下的漏洞。而你通过窗口进入就是进行攻击,偷看人家老婆泡澡是你的目的。 用最形象易懂的方式解释什么是网络漏洞 用官话说:漏洞是…...

    2024/4/24 20:11:54
  13. java中的异常 总结

    什么是异常? java中的异常又称为例外,一个在程序执行期间发生的事件,它中断正在执行程序的正常指令流。 任何人都无法保证程序永远准确运行,就像谁也不知道明天和意外哪个先来。 程序一旦出现异常,那么就像现实中碰到了异常问题一样,需要得到及时的处理。 所以说对异常的…...

    2024/4/22 19:51:17
  14. 28、JQuery框架的简单使用

    右键WebCont,新建scripts文件夹 将jquery-1.7.2.min.js复制到这文件里 修改index.jsp (测试用的) <%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DT…...

    2024/5/2 0:50:16
  15. Spring-aop的实现原理简析与具体实现方式简介

    Spring-aop的实现原理简析与具体实现方式简介 ICO与AOP是Spring最终要的编程“思想”,在最近的学习过程中能够充分感受到编程思想的转变对编程效能提升的巨大推动力。本篇文章对实现AOP的三种基本方式进行一个简单的小节:aop的实现原理 aop的底层实现原理是“动态代理模式”,…...

    2024/4/24 12:38:24
  16. 学习笔记(15):70讲轻松通关JavaSE-系统环境变量

    立即学习:https://edu.csdn.net/course/play/30002/432838?utm_source=blogtoedupath环境变量:path环境变量是系统环境变量中的一种,它用于保存一系列的路径,每个路径之间以分号分隔,当在命令行窗口运行一个可执行文件时,操作系统首先会在当前目录下查找是否存在该文件,…...

    2024/4/11 19:35:33
  17. Hyperledger fabric 2.0(文档7.1 Deploying a smart contract to a channel)

    写在前面:因为之前一直在使用1.0.0和1.4版本,文档500多页看到 key concepts就已经感觉这个文档是没有办法看完的,但是在网上照着很多博主的文章去做,还是会有很多错误,最后还是继续做死 做人,照着文档去做 本文是根据hyperledger fabric 2.0文档进行 前面五个步骤安装go、…...

    2024/4/29 16:14:21
  18. 【无人机】【2017】基于无人机的优化终端交付

    本文为美国明尼苏达州立大学(作者:Fuad Gazal)的硕士论文,共112页。 无人机(UAV)是一种遥控飞行器,有着广泛的应用。尽管无人机早期的应用主要集中在军事上,但监视、摄影和农业应用目前正在兴起。这项工作的目的是确定如何使用无人机来减少运输时间、提高动力效率和提高…...

    2024/4/27 23:44:58
  19. ftp工具,简易ftp工具

    ftp工具是一种文件传输下载方式,它是TCP/IP协议栈的一部分;其中FTP又由两部分组成,一部分是FTP的服务器,另一部分是FTP的客户端!它能够高效安全地进行文件传输下载操作!可以使用服务器管理工具来作为FTP的客户端,进行FTP的操作,实现FTP的下载安装。所以ftp工具的选择也…...

    2024/4/25 3:23:54
  20. Snort预处理器之`Modbus`

    文章目录1 Modbus 预处理器简介2 Modbus 预处理器的依赖3 Modbus 预处理器的配置4 Modbus 预处理器的规则选项5 Modbus 预处理器的事件 1 Modbus 预处理器简介 Modbus 预处理器是一个对 Modbus 协议进行解码的 Snort 模块。它还提供了访问某些协议字段的规则选项。这允许用户为…...

    2024/4/26 21:37:28

最新文章

  1. 小程序如何重启

    用户在使用小程序的过程中&#xff0c;有时候会碰到一些问题。比如小程序数据不加载、卡顿、崩溃或者出现其他异常情况。这时候&#xff0c;最简单的办法就是重启小程序。但是很多客户不知道如何重启小程序&#xff0c;下面就具体介绍小程序重新启动的几种方法。 1. 强制关闭&…...

    2024/5/6 21:30:04
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/5/6 9:38:23
  3. Android如何实现一个应用位于前台时全局页面每隔三分钟弹出一次一天最多弹出5次的GroMore半插屏广告,处于付费页和后台时停止

    首先我们需要添加一个全局的Application public class MyApp extends LitePalApplication {private static final String TAG "MyApp";private static Context mContext;private boolean isManageMent;public static String oaid;Overridepublic void onCreate() {…...

    2024/5/3 5:28:16
  4. Mac brew 安装软件

    Mac brew 安装软件 homebrew 速度慢 将brew 切换到国内镜像源 # 速度一般 # 步骤一 cd "$(brew --repo)" git remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git# 步骤二 cd "$(brew --repo)/Library/Taps/homebrew/homebr…...

    2024/5/3 9:32:52
  5. 多态--下

    文章目录 概念多态如何实现的指向谁调谁&#xff1f;例子分析 含有虚函数类的大小是多少&#xff1f;虚函数地址虚表地址多继承的子类的大小怎么计算&#xff1f;练习题虚函数和虚继承 概念 优先使用组合、而不是继承; 继承会破坏父类的封装、因为子类也可以调用到父类的函数;…...

    2024/5/5 8:35:37
  6. 416. 分割等和子集问题(动态规划)

    题目 题解 class Solution:def canPartition(self, nums: List[int]) -> bool:# badcaseif not nums:return True# 不能被2整除if sum(nums) % 2 ! 0:return False# 状态定义&#xff1a;dp[i][j]表示当背包容量为j&#xff0c;用前i个物品是否正好可以将背包填满&#xff…...

    2024/5/6 18:23:10
  7. 【Java】ExcelWriter自适应宽度工具类(支持中文)

    工具类 import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet;/*** Excel工具类** author xiaoming* date 2023/11/17 10:40*/ public class ExcelUti…...

    2024/5/6 18:40:38
  8. Spring cloud负载均衡@LoadBalanced LoadBalancerClient

    LoadBalance vs Ribbon 由于Spring cloud2020之后移除了Ribbon&#xff0c;直接使用Spring Cloud LoadBalancer作为客户端负载均衡组件&#xff0c;我们讨论Spring负载均衡以Spring Cloud2020之后版本为主&#xff0c;学习Spring Cloud LoadBalance&#xff0c;暂不讨论Ribbon…...

    2024/5/5 19:59:54
  9. TSINGSEE青犀AI智能分析+视频监控工业园区周界安全防范方案

    一、背景需求分析 在工业产业园、化工园或生产制造园区中&#xff0c;周界防范意义重大&#xff0c;对园区的安全起到重要的作用。常规的安防方式是采用人员巡查&#xff0c;人力投入成本大而且效率低。周界一旦被破坏或入侵&#xff0c;会影响园区人员和资产安全&#xff0c;…...

    2024/5/6 7:24:07
  10. VB.net WebBrowser网页元素抓取分析方法

    在用WebBrowser编程实现网页操作自动化时&#xff0c;常要分析网页Html&#xff0c;例如网页在加载数据时&#xff0c;常会显示“系统处理中&#xff0c;请稍候..”&#xff0c;我们需要在数据加载完成后才能继续下一步操作&#xff0c;如何抓取这个信息的网页html元素变化&…...

    2024/5/5 15:25:47
  11. 【Objective-C】Objective-C汇总

    方法定义 参考&#xff1a;https://www.yiibai.com/objective_c/objective_c_functions.html Objective-C编程语言中方法定义的一般形式如下 - (return_type) method_name:( argumentType1 )argumentName1 joiningArgument2:( argumentType2 )argumentName2 ... joiningArgu…...

    2024/5/6 6:01:13
  12. 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】

    &#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】&#x1f30f;题目描述&#x1f30f;输入格…...

    2024/5/6 7:24:06
  13. 【ES6.0】- 扩展运算符(...)

    【ES6.0】- 扩展运算符... 文章目录 【ES6.0】- 扩展运算符...一、概述二、拷贝数组对象三、合并操作四、参数传递五、数组去重六、字符串转字符数组七、NodeList转数组八、解构变量九、打印日志十、总结 一、概述 **扩展运算符(...)**允许一个表达式在期望多个参数&#xff0…...

    2024/5/6 1:08:53
  14. 摩根看好的前智能硬件头部品牌双11交易数据极度异常!——是模式创新还是饮鸩止渴?

    文 | 螳螂观察 作者 | 李燃 双11狂欢已落下帷幕&#xff0c;各大品牌纷纷晒出优异的成绩单&#xff0c;摩根士丹利投资的智能硬件头部品牌凯迪仕也不例外。然而有爆料称&#xff0c;在自媒体平台发布霸榜各大榜单喜讯的凯迪仕智能锁&#xff0c;多个平台数据都表现出极度异常…...

    2024/5/6 20:04:22
  15. Go语言常用命令详解(二)

    文章目录 前言常用命令go bug示例参数说明 go doc示例参数说明 go env示例 go fix示例 go fmt示例 go generate示例 总结写在最后 前言 接着上一篇继续介绍Go语言的常用命令 常用命令 以下是一些常用的Go命令&#xff0c;这些命令可以帮助您在Go开发中进行编译、测试、运行和…...

    2024/5/6 0:27:44
  16. 用欧拉路径判断图同构推出reverse合法性:1116T4

    http://cplusoj.com/d/senior/p/SS231116D 假设我们要把 a a a 变成 b b b&#xff0c;我们在 a i a_i ai​ 和 a i 1 a_{i1} ai1​ 之间连边&#xff0c; b b b 同理&#xff0c;则 a a a 能变成 b b b 的充要条件是两图 A , B A,B A,B 同构。 必要性显然&#xff0…...

    2024/5/6 7:24:04
  17. 【NGINX--1】基础知识

    1、在 Debian/Ubuntu 上安装 NGINX 在 Debian 或 Ubuntu 机器上安装 NGINX 开源版。 更新已配置源的软件包信息&#xff0c;并安装一些有助于配置官方 NGINX 软件包仓库的软件包&#xff1a; apt-get update apt install -y curl gnupg2 ca-certificates lsb-release debian-…...

    2024/5/6 7:24:04
  18. Hive默认分割符、存储格式与数据压缩

    目录 1、Hive默认分割符2、Hive存储格式3、Hive数据压缩 1、Hive默认分割符 Hive创建表时指定的行受限&#xff08;ROW FORMAT&#xff09;配置标准HQL为&#xff1a; ... ROW FORMAT DELIMITED FIELDS TERMINATED BY \u0001 COLLECTION ITEMS TERMINATED BY , MAP KEYS TERMI…...

    2024/5/6 19:38:16
  19. 【论文阅读】MAG:一种用于航天器遥测数据中有效异常检测的新方法

    文章目录 摘要1 引言2 问题描述3 拟议框架4 所提出方法的细节A.数据预处理B.变量相关分析C.MAG模型D.异常分数 5 实验A.数据集和性能指标B.实验设置与平台C.结果和比较 6 结论 摘要 异常检测是保证航天器稳定性的关键。在航天器运行过程中&#xff0c;传感器和控制器产生大量周…...

    2024/5/6 7:24:03
  20. --max-old-space-size=8192报错

    vue项目运行时&#xff0c;如果经常运行慢&#xff0c;崩溃停止服务&#xff0c;报如下错误 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory 因为在 Node 中&#xff0c;通过JavaScript使用内存时只能使用部分内存&#xff08;64位系统&…...

    2024/5/5 17:03:52
  21. 基于深度学习的恶意软件检测

    恶意软件是指恶意软件犯罪者用来感染个人计算机或整个组织的网络的软件。 它利用目标系统漏洞&#xff0c;例如可以被劫持的合法软件&#xff08;例如浏览器或 Web 应用程序插件&#xff09;中的错误。 恶意软件渗透可能会造成灾难性的后果&#xff0c;包括数据被盗、勒索或网…...

    2024/5/6 21:25:34
  22. JS原型对象prototype

    让我简单的为大家介绍一下原型对象prototype吧&#xff01; 使用原型实现方法共享 1.构造函数通过原型分配的函数是所有对象所 共享的。 2.JavaScript 规定&#xff0c;每一个构造函数都有一个 prototype 属性&#xff0c;指向另一个对象&#xff0c;所以我们也称为原型对象…...

    2024/5/6 7:24:02
  23. C++中只能有一个实例的单例类

    C中只能有一个实例的单例类 前面讨论的 President 类很不错&#xff0c;但存在一个缺陷&#xff1a;无法禁止通过实例化多个对象来创建多名总统&#xff1a; President One, Two, Three; 由于复制构造函数是私有的&#xff0c;其中每个对象都是不可复制的&#xff0c;但您的目…...

    2024/5/6 7:24:01
  24. python django 小程序图书借阅源码

    开发工具&#xff1a; PyCharm&#xff0c;mysql5.7&#xff0c;微信开发者工具 技术说明&#xff1a; python django html 小程序 功能介绍&#xff1a; 用户端&#xff1a; 登录注册&#xff08;含授权登录&#xff09; 首页显示搜索图书&#xff0c;轮播图&#xff0…...

    2024/5/5 17:03:21
  25. 电子学会C/C++编程等级考试2022年03月(一级)真题解析

    C/C++等级考试(1~8级)全部真题・点这里 第1题:双精度浮点数的输入输出 输入一个双精度浮点数,保留8位小数,输出这个浮点数。 时间限制:1000 内存限制:65536输入 只有一行,一个双精度浮点数。输出 一行,保留8位小数的浮点数。样例输入 3.1415926535798932样例输出 3.1…...

    2024/5/6 16:50:57
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57