springboot 集成OAuth2 一些用到的基础类

1. 登录校验token,提取clientId

package org.springframework.security.web.authentication.www;
public class BasicAuthenticationConverter implements AuthenticationConverter {
	// 主要方法
	 public UsernamePasswordAuthenticationToken convert(HttpServletRequest request) {
        String header = request.getHeader("Authorization");
        if (header == null) {
            return null;
        } else {
            header = header.trim();
            if (!StringUtils.startsWithIgnoreCase(header, "Basic")) {
                return null;
            } else if (header.equalsIgnoreCase("Basic")) {
                throw new BadCredentialsException("Empty basic authentication token");
            } else {
                byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);
                byte[] decoded = this.decode(base64Token);
                String token = new String(decoded, this.getCredentialsCharset(request));
                int delim = token.indexOf(":");
                if (delim == -1) {
                    throw new BadCredentialsException("Invalid basic authentication token");
                } else {
                    UsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken.unauthenticated(token.substring(0, delim), token.substring(delim + 1));
                    result.setDetails(this.authenticationDetailsSource.buildDetails(request));
                    return result;
                }
            }
        }
    }
}

2. 根据clientId 获取token配置信息类

// 自定义类,继承JdbcClientDetailsService 或 ClientDetailsService
public class RedisClientDetailsService extends JdbcClientDetailsService {

	/**
	 * 重写 loadClientByClientId 方法,从缓存中获取客户端的配置信息
	 */		
	@Override
    public ClientDetails loadClientByClientId(String clientId) throws InvalidClientException {
        ClientDetails clientDetails = null;
        String value = RedisUtil.hGet(CACHE_CLIENT_KEY, clientId, RedisDBType.PRIMARY);
        if (StringUtils.isBlank(value)) {
            clientDetails = cacheAndGetClient(clientId);
        } else {
            clientDetails = JSONObject.parseObject(value, BaseClientDetails.class);
        }
    
        return clientDetails;
    }
}

3. 实际登录类

package org.springframework.security.oauth2.provider.endpoint;
public class TokenEndpoint extends AbstractEndpoint {

	/**
	 * 登录方法,获取token信息
	 */		
	@RequestMapping(value = "/oauth/token", method=RequestMethod.GET)
	public ResponseEntity<OAuth2AccessToken> getAccessToken(Principal principal, @RequestParam
	Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
		if (!allowedRequestMethods.contains(HttpMethod.GET)) {
			throw new HttpRequestMethodNotSupportedException("GET");
		}
		return postAccessToken(principal, parameters);
	}
	
	@RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
	public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam
	Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {

		if (!(principal instanceof Authentication)) {
			throw new InsufficientAuthenticationException(
					"There is no client authentication. Try adding an appropriate authentication filter.");
		}

		String clientId = getClientId(principal);
		ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);

		TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);

		if (clientId != null && !clientId.equals("")) {
			// Only validate the client details if a client authenticated during this
			// request.
			if (!clientId.equals(tokenRequest.getClientId())) {
				// double check to make sure that the client ID in the token request is the same as that in the
				// authenticated client
				throw new InvalidClientException("Given client ID does not match authenticated client");
			}
		}
		if (authenticatedClient != null) {
			oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
		}
		if (!StringUtils.hasText(tokenRequest.getGrantType())) {
			throw new InvalidRequestException("Missing grant type");
		}
		if (tokenRequest.getGrantType().equals("implicit")) {
			throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
		}

		if (isAuthCodeRequest(parameters)) {
			// The scope was requested or determined during the authorization step
			if (!tokenRequest.getScope().isEmpty()) {
				logger.debug("Clearing scope of incoming token request");
				tokenRequest.setScope(Collections.<String> emptySet());
			}
		}

		if (isRefreshTokenRequest(parameters)) {
			// A refresh token has its own default scopes, so we should ignore any added by the factory here.
			tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
		}

		OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
		if (token == null) {
			throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
		}

		return getResponse(token);

	}
}

4. 加载token信息到自定义缓存

/**
 * 重写类,实现 TokenStore 类
 */
public class MyRedisTokenStore implements TokenStore {

	@Override
    public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
		String key = authenticationKeyGenerator.extractKey(authentication);
        byte[] serializedKey = serializeKey(FebsConstant.AUTH_TO_ACCESS + key);
        byte[] bytes = null;
        RedisConnection conn = getConnection();
        try {
            bytes = conn.get(serializedKey);
        } finally {
            conn.close();
        }
        OAuth2AccessToken accessToken = deserializeAccessToken(bytes);
        if (accessToken != null) {
            OAuth2Authentication storedAuthentication = readAuthentication(accessToken.getValue());
            if ((storedAuthentication == null || !key.equals(authenticationKeyGenerator.extractKey(storedAuthentication)))) {
                // 将token 信息加载到缓存中
                storeAccessToken(accessToken, authentication);
            }

        }
        return accessToken;
	}

	@Override
	public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
		// 根据token 与 authentication信息自定义缓存
		// ...
	}

	/**
 	 * 重写方法,自定义根据token获取 authentication 信息
 	 */	
	@Override
    public OAuth2Authentication readAuthentication(String token) {
        byte[] bytes = null;
        RedisConnection conn = getConnection();
        try {
            bytes = conn.get(serializeKey(FebsConstant.AUTH + token));
        } finally {
            conn.close();
        }
        OAuth2Authentication auth = deserializeAuthentication(bytes);
        return auth;
    }

	/**
 	 * 重写方法,删除token信息
 	 */	
	@Override
    public OAuth2AccessToken removeAccessToken(String tokenValue){

	}
}

5. 自定义用户信息类

/**
 * 重写类,实现 UserDetailsService 类
 */
public class MyUserDetailServiceImpl implements UserDetailsService {

	@Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// 自定义:可写mapper查询出用户信息,放入UserDeatail对象中返回
	}
}

6. 异常翻译类

/**
 * 异常翻译 实现 WebResponseExceptionTranslator 类
 */
public class FebsWebResponseExceptionTranslator implements WebResponseExceptionTranslator {

    @Override
    public ResponseEntity<?> translate(Exception e) {
        ResponseEntity.BodyBuilder status = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
        FebsResponse response = new FebsResponse();
        String message = "认证失败";
        log.error(message, e);
        if (e instanceof UnsupportedGrantTypeException) {
            message = "不支持该认证类型";
            return status.body(response.message(message));
        }
        if (e instanceof InvalidTokenException
                && StringUtils.containsIgnoreCase(e.getMessage(), "Invalid refresh token (expired)")) {
            message = "刷新令牌已过期,请重新登录";
            return status.body(response.message(message));
        }
        if (e instanceof InvalidScopeException) {
            message = "不是有效的scope值";
            return status.body(response.message(message));
        }
        if (e instanceof RedirectMismatchException) {
            message = "redirect_uri值不正确";
            return status.body(response.message(message));
        }
        if (e instanceof BadClientCredentialsException) {
            message = "client值不合法";
            return status.body(response.message(message));
        }
        if (e instanceof UnsupportedResponseTypeException) {
            String code = StringUtils.substringBetween(e.getMessage(), "[", "]");
            message = code + "不是合法的response_type值";
            return status.body(response.message(message));
        }
        if (e instanceof InvalidGrantException) {
            if (StringUtils.containsIgnoreCase(e.getMessage(), "Invalid refresh token")) {
                message = "refresh token无效";
                return status.body(response.message(message));
            }
            if (StringUtils.containsIgnoreCase(e.getMessage(), "Invalid authorization code")) {
                String code = StringUtils.substringAfterLast(e.getMessage(), ": ");
                message = "授权码" + code + "不合法";
                return status.body(response.message(message));
            }
            if (StringUtils.containsIgnoreCase(e.getMessage(), "locked")) {
                message = "用户已被锁定,请联系管理员";
                return status.body(response.message(message));
            }
            message = "用户名或密码错误";
            return status.body(response.message(message));
        }
        return status.body(response.message(message));
    }
}

7. 认证服务器配置

/**
 * 认证服务器配置
 */
public class MyAuthorizationServerConfigure extends AuthorizationServerConfigurerAdapter {
	
	/**
     * token 检查策略
     */
	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    	security.checkTokenAccess("permitAll()");
	}

	/**
     * 客户端配置
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		// 该对象是2
        clients.withClientDetails(redisClientDetailsService);
    }

	/**
     * token 携带额外信息
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer()));

        endpoints.tokenStore(tokenStore()) // token信息记录方式
        		.tokenEnhancer(tokenEnhancerChain)
                .userDetailsService(userDetailService) // 对象5
                .authorizationCodeServices(authenticationCodeService) // 咱不知道用途
                .authenticationManager(authenticationManager) // 咱不知道用途
                .exceptionTranslator(exceptionTranslator); // 对象6
		// 是否开启jwt认证
        if (properties.getEnableJwt()) {
            endpoints.accessTokenConverter(jwtAccessTokenConverter());
        }
    }

	/**
     * 注入TokenStore
     */
	@Bean
    public TokenStore tokenStore() {
		// 是否开启jwt认证
        if (properties.getEnableJwt()) {
            return new JwtTokenStore(jwtAccessTokenConverter());
        } else {
            // 使用自定义类 对象4
            MyRedisTokenStore redisTokenStore = new MyRedisTokenStore(redisConnectionFactory);
            // 解决每次生成的 token都一样的问题
            redisTokenStore.setAuthenticationKeyGenerator(oAuth2Authentication -> UUID.randomUUID().toString());
            return redisTokenStore;
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot集成OAuth2可以实现用户身份验证和授权功能。OAuth2是一种用于授权的开放标准,允许用户授权第三方应用访问他们在其他服务提供商上的资源,而无需将用户名和密码提供给第三方应用。 要在Spring Boot应用程序中实现OAuth2,可以使用Spring Security和Spring Security OAuth2模块。下面是一个简单的步骤指南: 1. 添加依赖:在Maven或Gradle中添加Spring Security和Spring Security OAuth2的相关依赖。 2. 配置认证服务器:创建一个认证服务器配置,继承自`AuthorizationServerConfigurerAdapter`,并重写`configure`方法来配置OAuth2的相关信息,如授权型、客户端信息、访问令牌的有效期等。 3. 配置资源服务器:创建一个资源服务器配置,继承自`ResourceServerConfigurerAdapter`,并重写`configure`方法来配置受保护的资源路径和访问规则。 4. 创建用户认证服务:创建一个实现了`UserDetailsService`接口的自定义用户认证服务,用于从数据库或其他数据源中获取用户信息。 5. 配置Spring Security:创建一个继承自`WebSecurityConfigurerAdapter`的配置,通过重写`configure`方法来配置Spring Security的行为,例如允许所有用户访问某些路径或强制用户进行身份验证。 6. 配置安全规则:在应用程序的配置文件(如application.properties)中,配置安全规则,如禁用CSRF保护、允许访问的路径等。 7. 创建登录页面:根据需要创建一个登录页面,用于用户进行身份验证。 以上是一个基本的步骤指南,具体实现可能会根据你的应用程序需求而有所不同。你可以参考Spring官方文档和示例代码来更详细地了解和实现Spring Boot集成OAuth2的过程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值