1、继承UsernamePasswordAuthenticationToken,自定义接收参数
package com.mx.octoo.auth.authentication; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import java.util.Collection; public class TenantAuthenticationToken extends UsernamePasswordAuthenticationToken { private static final long serialVersionUID = 8217066478491458584L; private final String tenantCode; // ~ Constructors // =================================================================================================== /** * This constructor can be safely used by any code that wishes to create a * <code>UsernamePasswordAuthenticationToken</code>, as the {@link #isAuthenticated()} * will return <code>false</code>. * */ public TenantAuthenticationToken(String principal, String credentials, String tenantCode) { super(principal,credentials ); this.tenantCode = tenantCode; setAuthenticated(false); } /** * This constructor should only be used by <code>AuthenticationManager</code> or * <code>AuthenticationProvider</code> implementations that are satisfied with * producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>) * authentication token. * * @param principal * @param credentials * @param authorities */ public TenantAuthenticationToken(String principal, String credentials, String tenantCode, Collection<? extends GrantedAuthority> authorities) { super(principal,credentials,authorities ); this.tenantCode = tenantCode; super.setAuthenticated(true); } public String getTenantCode() { return tenantCode; } }
2、 自定义TokenGranter可以实现获取除了默认参数的其他参数,同时可以增加验证
package com.mx.octoo.auth.granter; import com.mx.octoo.auth.authentication.TenantAuthenticationToken; import org.springframework.security.authentication.*; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; import org.springframework.security.oauth2.provider.*; import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import java.util.LinkedHashMap; import java.util.Map; public class TenantTokenGranter extends ResourceOwnerPasswordTokenGranter { private static final String GRANT_TYPE = "password"; private final AuthenticationManager authenticationManager; public TenantTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) { this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE); } protected TenantTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) { super(authenticationManager, tokenServices, clientDetailsService, requestFactory, grantType); this.authenticationManager = authenticationManager; } @Override protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) { Map<String, String> parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters()); String username = parameters.get("username"); String password = parameters.get("password"); String tenantCode = parameters.get("tenantCode"); // Protect from downstream leaks of password parameters.remove("password"); Authentication userAuth = new TenantAuthenticationToken(username, password, tenantCode); ((AbstractAuthenticationToken) userAuth).setDetails(parameters); try { userAuth = authenticationManager.authenticate(userAuth); } catch (AccountStatusException | BadCredentialsException ase) { throw new InvalidGrantException(ase.getMessage()); } if (userAuth == null || !userAuth.isAuthenticated()) { throw new InvalidGrantException("Could not authenticate user: " + username); } OAuth2Request oAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest); return new OAuth2Authentication(oAuth2Request, userAuth); } }
3、自定义认证体 AbstractUserDetailsAuthenticationProvider,实现自定义身份验证器
import com.mx.octoo.auth.authentication.TenantAuthenticationToken; import com.mx.octoo.auth.service.UserDetailsService; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.InternalAuthenticationServiceException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.util.Assert; public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword"; private PasswordEncoder passwordEncoder; private volatile String userNotFoundEncodedPassword; private final UserDetailsService userDetailsService; public DaoAuthenticationProvider(UserDetailsService userDetailsService) { setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()); this.userDetailsService = userDetailsService; } @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { return super.authenticate(authentication); } @Override protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { if (authentication.getCredentials() == null) { logger.debug("Authentication failed: no credentials provided"); throw new BadCredentialsException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } String presentedPassword = authentication.getCredentials().toString(); if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { logger.debug("Authentication failed: password does not match stored value"); throw new BadCredentialsException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } } @Override protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { prepareTimingAttackProtection(); TenantAuthenticationToken token = (TenantAuthenticationToken) authentication; try { UserDetails loadedUser = userDetailsService.loadUserByUsername(username, token.getTenantCode()); if (loadedUser == null) { throw new InternalAuthenticationServiceException( "UserDetailsService returned null, which is an interface contract violation"); } return loadedUser; } catch (UsernameNotFoundException ex) { mitigateAgainstTimingAttack(authentication); throw ex; } catch (InternalAuthenticationServiceException ex) { throw ex; } catch (Exception ex) { throw new InternalAuthenticationServiceException(ex.getMessage(), ex); } } private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) { if (authentication.getCredentials() != null) { String presentedPassword = authentication.getCredentials().toString(); this.passwordEncoder.matches(presentedPassword, this.userNotFoundEncodedPassword); } } public void setPasswordEncoder(PasswordEncoder passwordEncoder) { Assert.notNull(passwordEncoder, "passwordEncoder cannot be null"); this.passwordEncoder = passwordEncoder; this.userNotFoundEncodedPassword = null; } private void prepareTimingAttackProtection() { if (this.userNotFoundEncodedPassword == null) { this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD); } } }
4、注册认证器DaoAuthenticationProvider
package com.mx.octoo.auth.config.auth; import com.mx.octoo.auth.dao.DaoAuthenticationProvider; import com.mx.octoo.auth.service.UserDetailsService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.annotation.Order; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.PasswordEncoder; @Primary @Order(90) @Configuration public class OctooSecurityConfig extends WebSecurityConfigurerAdapter { private final UserDetailsService userDetailsService; public OctooSecurityConfig(UserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } @Override protected void configure(AuthenticationManagerBuilder auth) { auth.authenticationProvider(new DaoAuthenticationProvider(userDetailsService)); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/actuator/**", "/oauth/token", "/oauth/logout", "/oauth/logout/user", "/swagger-ui.html", "/v2/api-docs") .permitAll().anyRequest().authenticated().and().csrf().disable(); } @Bean @Override protected AuthenticationManager authenticationManager() throws Exception { return super.authenticationManager(); } /** * 参照 PasswordEncoderFactories.createDelegatingPasswordEncoder() * @return PasswordEncoder */ @Bean public PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); } }
5、自定义实现UserDetailsService
package com.mx.octoo.auth.service; import com.mx.octoo.uc.api.entity.UserEntity; import com.mx.octoo.uc.api.feign.IRemoteUserService; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @Slf4j @Service @AllArgsConstructor public class UserDetailsService { private final IRemoteUserService userService; public UserDetails loadUserByUsername(String username, String tenantCode) throws UsernameNotFoundException { UserEntity user = userService.findByUserName(username, tenantCode); return new User(user.getUsername(), user.getPassword(), AuthorityUtils.NO_AUTHORITIES); } }
6、注册TenantTokenGranter授权模式
package com.mx.octoo.auth.config.auth; import com.mx.octoo.auth.service.UserDetailsService; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.CompositeTokenGranter; import org.springframework.security.oauth2.provider.TokenGranter; import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter; import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter; import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter; import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter; import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter; import javax.sql.DataSource; import java.util.ArrayList; import java.util.List; @RequiredArgsConstructor @Configuration public class OctooAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { private final DataSource authDataSource; private final AuthenticationManager authenticationManager; @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.jdbc(authDataSource); } @Override public void configure(AuthorizationServerSecurityConfigurer security) { security.tokenKeyAccess("permitAll()") .checkTokenAccess("permitAll()"). allowFormAuthenticationForClients(); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints .tokenGranter(tokenGranter(endpoints)) .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST); } private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) { List<TokenGranter> list = new ArrayList<>(); // 自定义配置租户密码模式 if (authenticationManager != null) { list.add(new TenantTokenGranter(authenticationManager, endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory())); } //刷新token模式 list.add(new RefreshTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory())); //授权码模式 list.add(new AuthorizationCodeTokenGranter( endpoints.getTokenServices(), endpoints.getAuthorizationCodeServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory())); //、简化模式 list.add(new ImplicitTokenGranter( endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory())); //客户端模式 list.add(new ClientCredentialsTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory())); return new CompositeTokenGranter(list); } }
来源:oschina
链接:https://my.oschina.net/u/3756051/blog/4839791