一、通過OAuth2 Toke的Scope參數控制權限
1,在服務端認證服務器里,通過配置客戶端的Scope,可以控制給這個客戶端生成的token有哪些權限
2,在客戶端,申請令牌的時候,可以指定scope
示例:在資源服務器 (nb-order-api)里,控制post請求的token ,其scope必須包含write權限,get請求的token必須包含read權限。
用postman客戶端(clientId=orderApp)去認證服務器申請一個scpoe=read的token,去調用資源服務器里的Post請求:
調用這個創建訂單的Post請求:
返回錯誤信息:
二,將token轉換為用戶信息
目前在資源服務器里,想要獲取用戶信息,在Controller里,可以通過 @AuthenticationPrincipal 注解,獲取生成token的用戶名。但是獲取不到用戶的其他信息,如userId等。
做如下修改:
1,在資源服務器的安全配置: OAuth2WebSecurityConfig 里,的 tokenServices方法里,配置一個 AccessTokenConverter,用來將token信息轉換為 User 信息
2,新建UserDetailsService的實現類
OAuth2WebSecurityConfig:
package com.nb.security.resource.server; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager; import org.springframework.security.oauth2.provider.token.*; /** * 怎么驗發往本服務的請求頭的令牌 * 1,自定義tokenServices ,說明去哪里去驗token * 2,重寫authenticationManagerBean()方法,將AuthenticationManager暴露為一個Bean * 要認證跟用戶相關的信息,一般用 AuthenticationManager * * 這樣配置了后,所有發往nb-order-api的請求, * 需要驗token的時候就會發請求去http://localhost:9090/oauth/check_token驗token,獲取到token對應的用戶信息 */ @Configuration @EnableWebSecurity public class OAuth2WebSecurityConfig extends WebSecurityConfigurerAdapter{ @Autowired private UserDetailsService userDetailsService; /** * 通過這個Bean,去遠程調用認證服務器,驗token * @return */ @Bean public ResourceServerTokenServices tokenServices(){ RemoteTokenServices tokenServices = new RemoteTokenServices(); tokenServices.setClientId("orderService");//在認證服務器配置的,訂單服務的clientId tokenServices.setClientSecret("123456");//在認證服務器配置的,訂單服務的ClientSecret tokenServices.setCheckTokenEndpointUrl("http://localhost:9090/oauth/check_token"); //配置一個轉換器,將token信息轉換為用戶對象 // TODO:獲取用戶信息本應該是認證服務器的事吧!總感覺在這里做不合適 tokenServices.setAccessTokenConverter(getAccessTokenConverter()); return tokenServices; } //轉換器,將token轉換為用戶信息 private AccessTokenConverter getAccessTokenConverter() { DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter(); //這個類的目的是設UserDetailsService,來將token轉換為用戶信息,不設默認為空 DefaultUserAuthenticationConverter userTokenConverter = new DefaultUserAuthenticationConverter(); userTokenConverter.setUserDetailsService(userDetailsService); accessTokenConverter.setUserTokenConverter(userTokenConverter); return accessTokenConverter; } /** * 要認證跟用戶相關的信息,一般用 AuthenticationManager * 覆蓋這個方法,可以將AuthenticationManager暴露為一個Bean * * @return * @throws Exception */ @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { OAuth2AuthenticationManager authenticationManager = new OAuth2AuthenticationManager(); authenticationManager.setTokenServices(tokenServices());//設置為自定義的TokenServices,去校驗令牌 return authenticationManager; } }
UserDetailsServiceImpl
package com.nb.security.resource.server; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; @Component("userDetailsService") //TODO:不 public class UserDetailsServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //這里就不去讀數據庫了 User user = new User(); user.setId(1L); user.setUsername(username); return user; } }
User對象,實現UserDetails接口:
package com.nb.security.resource.server; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; public class User implements UserDetails{ private Long id; private String username; private String password; @Override public Collection<? extends GrantedAuthority> getAuthorities() { return AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN"); } @Override public boolean isAccountNonExpired() { return true; //賬號沒過期 } @Override public boolean isAccountNonLocked() { return true;//賬號沒被鎖定 } @Override public boolean isCredentialsNonExpired() { return true;//密碼沒過期 } @Override public boolean isEnabled() { return true;//是否可用 } @Override public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Override public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } }
然后在訂單Controller里,就可以取到用戶的 id等其他屬性了:
用 @AuthenticationPrincipal User user 注解可以取出User對象。
用 @AuthenticationPrincipal(expression = "#this.id") Long id 可以取出User里面的屬性
代碼放在了github :https://github.com/lhy1234/springcloud-security/tree/chapt-4-6-config-persistence
下一節,說將token持久化到數據庫
歡迎關注個人公眾號一起交流學習: