springsecurity第三方授权认证的项目实践
作者:爱发博客的嗯哼
由于博主在做一个校园项目的时候使用啦spring security安全框架,然后在整合第三方授权登录的时候,被困扰了好几天,就想着发一下这个文章,希望能给大家带来帮助。
第三方授权登录的原理,我就不在这里过多阐述了(作者也是小白,怕给你们带入歧途),大家不熟悉或者不了解的可以取哔哩哔哩看一下不良人的springsecurity教程,后面的课程就是讲述的第三方授权登录的知识。也可以看一下《深入浅出spring security》这本书。
单纯的springboot项目的话可以直接使用justauth这个第三方框架,集成啦许多的第三方登录的接口,只用自己调用一下api就能解决第三方授权登录的问题。
问题描述
现在大多数软件和web网站都会加入第三方授权登录的功能,以方便提高用户的体验。此时就给我们后端开发的人带来了极大的烦恼。
由于此项目加入了springsecurity框架,此时就不能使用justauth这个框架了,这个框架关于springsecurity的解决还没完善。所以就要使用springsecurity自带的oauth2认证过滤器(其实你会发现非常简单就能解决了)。
详细步骤(以gitee举例)
1. 进入官网点击设置
2. 向下翻转,点击第三方应用
3. 点击创建应用后就进入下面这个界面
4. 然后把标星的给填上
注意回调地址格式不要写错,否则springsecurity识别不出http://IP地址:端口号/login/oauth2/code/应用名称
5. 在springboot里加入配置
security: oauth2: client: registration: gitee: client-id: #授权id client-secret: #授权密钥 authorization-grant-type: authorization_code redirect-uri: #回调地址 client-name: gitee #应用名称 scope: user_info provider: gitee: authorization-uri: https://gitee.com/oauth/authorize token-uri: https://gitee.com/oauth/token user-info-uri: https://gitee.com/api/v5/user user-name-attribute: gitee
6. 此时要创建一个实体类,这个实体类是接收第三方应用传输的授权信息的。每个应用的授权信息都不同,大家可以在网上单独看一下对应的官网都会返回什么授权信息。
@NoArgsConstructor @AllArgsConstructor @Data public class OAuth2UserDTO implements OAuth2User { private String source; private String id; private String name; private String email; private String avatar; @JsonIgnore @JSONField(serialize = false) private List<GrantedAuthority> authorities= AuthorityUtils.createAuthorityList("ROLE_USER"); @JsonIgnore @JSONField(serialize = false) private Map<String,Object> attributes; @Override public Map<String, Object> getAttributes() { if (attributes==null){ attributes=new HashMap<>(); attributes.put("id",this.getId()); attributes.put("name",this.getName()); attributes.put("email",this.getEmail()); } return attributes; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return this.authorities; } public OAuth2UserDTO(Map<String,Object> attributes,String source){ this.attributes = attributes; this.source = source; this.id = attributes.get("id").toString(); this.name = attributes.get("name").toString(); this.email = attributes.get("email")==null?null:attributes.get("email").toString(); this.avatar = attributes.get("avatar_url")==null?null:attributes.get("avatar_url").toString(); } }
7. 重写oauth2认证方法
@Service public class CustomOAuth2UserService implements OAuth2UserService { private static final String MISSING_USER_INFO_URI_ERROR_CODE = "missing_user_info_uri"; private static final String MISSING_USER_NAME_ATTRIBUTE_ERROR_CODE = "missing_user_name_attribute"; private static final String INVALID_USER_INFO_RESPONSE_ERROR_CODE = "invalid_user_info_response"; private static final ParameterizedTypeReference<Map<String, Object>> PARAMETERIZED_RESPONSE_TYPE = new ParameterizedTypeReference<Map<String, Object>>() { }; private Converter<OAuth2UserRequest, RequestEntity<?>> requestEntityConverter = new OAuth2UserRequestEntityConverter(); private RestOperations restOperations; public CustomOAuth2UserService() { RestTemplate restTemplate = new RestTemplate(); restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler()); this.restOperations = restTemplate; } @Override public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { Assert.notNull(userRequest, "userRequest cannot be null"); if (!StringUtils .hasText(userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri())) { OAuth2Error oauth2Error = new OAuth2Error(MISSING_USER_INFO_URI_ERROR_CODE, "Missing required UserInfo Uri in UserInfoEndpoint for Client Registration: " + userRequest.getClientRegistration().getRegistrationId(), null); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); } String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint() .getUserNameAttributeName(); if (!StringUtils.hasText(userNameAttributeName)) { OAuth2Error oauth2Error = new OAuth2Error(MISSING_USER_NAME_ATTRIBUTE_ERROR_CODE, "Missing required \"user name\" attribute name in UserInfoEndpoint for Client Registration: " + userRequest.getClientRegistration().getRegistrationId(), null); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); } RequestEntity<?> request = this.requestEntityConverter.convert(userRequest); ResponseEntity<Map<String, Object>> response = getResponse(userRequest, request); Map<String, Object> userAttributes = response.getBody(); Set<GrantedAuthority> authorities = new LinkedHashSet<>(); authorities.add(new OAuth2UserAuthority(userAttributes)); OAuth2AccessToken token = userRequest.getAccessToken(); for (String authority : token.getScopes()) { authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority)); } //更换为自定义的OAuth2User实现 return new OAuth2UserDTO(userAttributes, userNameAttributeName); } private ResponseEntity<Map<String, Object>> getResponse(OAuth2UserRequest userRequest, RequestEntity<?> request) { OAuth2Error oauth2Error; try { return this.restOperations.exchange(request, PARAMETERIZED_RESPONSE_TYPE); } catch (OAuth2AuthorizationException var6) { oauth2Error = var6.getError(); StringBuilder errorDetails = new StringBuilder(); errorDetails.append("Error details: ["); errorDetails.append("UserInfo Uri: ").append(userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri()); errorDetails.append(", Error Code: ").append(oauth2Error.getErrorCode()); if (oauth2Error.getDescription() != null) { errorDetails.append(", Error Description: ").append(oauth2Error.getDescription()); } errorDetails.append("]"); oauth2Error = new OAuth2Error("invalid_user_info_response", "An error occurred while attempting to retrieve the UserInfo Resource: " + errorDetails.toString(), (String)null); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), var6); } catch (UnknownContentTypeException var7) { String errorMessage = "An error occurred while attempting to retrieve the UserInfo Resource from '" + userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri() + "': response contains invalid content type '" + var7.getContentType().toString() + "'. The UserInfo Response should return a JSON object (content type 'application/json') that contains a collection of name and value pairs of the claims about the authenticated End-User. Please ensure the UserInfo Uri in UserInfoEndpoint for Client Registration '" + userRequest.getClientRegistration().getRegistrationId() + "' conforms to the UserInfo Endpoint, as defined in OpenID Connect 1.0: 'https://openid.net/specs/openid-connect-core-1_0.html#UserInfo'"; OAuth2Error oAuth2Error = new OAuth2Error("invalid_user_info_response", errorMessage, (String)null); throw new OAuth2AuthenticationException(oAuth2Error, oAuth2Error.toString(), var7); } catch (RestClientException var8) { oauth2Error = new OAuth2Error("invalid_user_info_response", "An error occurred while attempting to retrieve the UserInfo Resource: " + var8.getMessage(), (String)null); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), var8); } } public final void setRequestEntityConverter(Converter<OAuth2UserRequest, RequestEntity<?>> requestEntityConverter) { Assert.notNull(requestEntityConverter, "requestEntityConverter cannot be null"); this.requestEntityConverter = requestEntityConverter; } public final void setRestOperations(RestOperations restOperations) { Assert.notNull(restOperations, "restOperations cannot be null"); this.restOperations = restOperations; } }
loadUser是核心方法,只用在业务上对其修改成符合自己的业务需求就行。
8. 配置securityConfig
http.oauth2Login() .userInfoEndpoint() .userService(new CustomOAuth2UserService());
9. 验证是否配置成功
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> </head> <body> <form action="/oauth2/authorization/gitee" method="post" onsubmit="onsubmitFun()"> <input type="submit" value="Gitee授权登录"> </form> </body> </html>
以上就配置成功了。更多相关springsecurity第三方授权认证内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!