- 添加maven依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
該依賴已包含Jwt相關Jar,所以不再添加jwt依賴
2.添加AuthServerConfig
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
protected JwtAccessTokenConverter jwtAccessTokenConverter() {
return new CustomJwtTokenConvert();
}
@Bean
public OAuth2ExceptionTranslator oAuth2ExceptionTranslator(){
return new OAuth2ExceptionTranslator();
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
endpoints.exceptionTranslator(oAuth2ExceptionTranslator());
.tokenStore(tokenStore()).accessTokenConverter(jwtAccessTokenConverter());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients()
.tokenKeyAccess("permitAll()") //獲取token不需要授權
.checkTokenAccess("permitAll()"); //檢查token不需要授權
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("user-service")
.scopes("userApi")
.secret("123456")
.authorizedGrantTypes("password", "authorization_code", "refresh_token")//授權類型,密碼模式/授權碼模式/刷新token
.accessTokenValiditySeconds(518400) //訪問令牌有效期
.refreshTokenValiditySeconds(604800) //刷新令牌有效期,需要比訪問令牌有效期長一點點,以便在訪問令牌過期後使用刷新令牌重新獲取
.and()
.withClient("order-service")
.scopes("orderApi")
.secret("123456")
.authorizedGrantTypes("password", "authorization_code", "refresh_token")
.accessTokenValiditySeconds(518400)
.refreshTokenValiditySeconds(604800);
}
}
3.自定義jwtTokenConvert
public class CustomJwtTokenConvert extends JwtAccessTokenConverter {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
if (accessToken instanceof DefaultOAuth2AccessToken) {
Object principal = authentication.getPrincipal();
if (principal instanceof OAuthUser) {
OAuthUser user = (OAuthUser) principal;
HashMap<String, Object> map = new HashMap<>();
map.put(UserConstants.USRE_ID, user.getUserDetail().getUserId());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(map);
}
}
return super.enhance(accessToken, authentication);
}
}
4.自定義用戶信息類
public class OAuthUser implements UserDetails, CredentialsContainer {
/**
* 這個userDetail是entity
*/
private final UserDetail userDetail;
private final User user;
public OAuthUser(UserDetail userDetail, User user) {
this.userDetail = userDetail;
this.user = user;
}
@Override
public void eraseCredentials() {
user.eraseCredentials();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities();
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return user.isAccountNonExpired();
}
@Override
public boolean isAccountNonLocked() {
return user.isAccountNonLocked();
}
@Override
public boolean isCredentialsNonExpired() {
return user.isCredentialsNonExpired();
}
@Override
public boolean isEnabled() {
return user.isEnabled();
}
public UserDetail getUserDetail() {
return userDetail;
}
}
5.自定義UserDetailsService繼承自spring security中的UserDetailsService
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserApi userApi;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetail userDetail=userApi.getByUsername(username).getBody();
List<? extends GrantedAuthority> authorities = new ArrayList();
return new OAuthUser(userDetail,new User(userDetail.getUsername(),userDetail.getPassword(),authorities));
}
}
6.自定義一個什麼都不做的密碼編碼器
public class NothingPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return rawPassword.toString().equals(encodedPassword);
}
public static PasswordEncoder getInstance() {
return INSTANCE;
}
private static final PasswordEncoder INSTANCE = new NothingPasswordEncoder();
private NothingPasswordEncoder() {
}
}
7.新建WebMvcConfig,用於放行和配置授權管理bean
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public UserDetailsService userDetailsService(){
return new CustomUserDetailsService();
}
@Bean
public PasswordEncoder passwordEncoder() {
return NothingPasswordEncoder.getInstance();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").permitAll();
}
}
8.因爲該服務也是一個資源服務,如果該服務有其他非授權類的接口,因oauth2依賴自動配置,接口不會被暴露,所以需要添加配置,說明該服務也是一個資源
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
.and()
.authorizeRequests()
.anyRequest().permitAll()
.and()
.httpBasic();
}
}
一般情況需要返回固定格式的對象,且feign調用如果密碼或者某些參數錯誤就會報400,401等運行時異常,新建一個OAuth2ExceptionTranslator來處理這些異常
public class OAuth2ExceptionTranslator implements WebResponseExceptionTranslator<OAuth2Exception> {
private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
@Override
public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
Throwable[] causeChain = throwableAnalyzer.determineCauseChain(e);
Throwable exception = throwableAnalyzer.getFirstThrowableOfType(InvalidGrantException.class, causeChain);
if(exception!=null){
return new ResponseEntity(OAuth2Exception.create(OAuth2Exception.INVALID_GRANT,"賬戶密碼錯誤"), HttpStatus.OK);
}
exception=throwableAnalyzer.getFirstThrowableOfType(InternalAuthenticationServiceException.class, causeChain);
if(exception!=null){
return new ResponseEntity(OAuth2Exception.create(OAuth2Exception.INVALID_GRANT,"賬戶名錯誤"), HttpStatus.OK);
}
exception=throwableAnalyzer.getFirstThrowableOfType(InvalidTokenException.class, causeChain);
if(exception!=null){
return new ResponseEntity(OAuth2Exception.create(OAuth2Exception.INVALID_TOKEN,"無效的訪問令牌"), HttpStatus.OK);
}
exception=throwableAnalyzer.getFirstThrowableOfType(InvalidClientException.class, causeChain);
if(exception!=null){
return new ResponseEntity(OAuth2Exception.create(OAuth2Exception.INVALID_CLIENT,null), HttpStatus.OK);
}
exception= throwableAnalyzer.getFirstThrowableOfType(InvalidScopeException.class, causeChain);
if(exception!=null){
return new ResponseEntity(OAuth2Exception.create(OAuth2Exception.INVALID_SCOPE,null), HttpStatus.OK);
}
return new ResponseEntity(OAuth2Exception.create(OAuth2Exception.INVALID_REQUEST,null), HttpStatus.OK);
}
}
9.該依賴屬於自動配置,所以配置文件與普通cloud服務沒太大區別,授權服務就搭建好了,下面看看怎麼調用
獲取token
檢查token
刷新token