Spring Security Oauth2配置类AuthorizationServerConfigurerAdapter

转:https://blog.csdn.net/silmeweed/article/details/101603227

https://www.cnblogs.com/fp2952/p/8973613.html

 

AuthorizationServerConfigurerAdapter中:

  • ClientDetailsServiceConfigurer:用来配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。
  • AuthorizationServerSecurityConfigurer:用来配置令牌端点(Token Endpoint)的安全约束.
  • AuthorizationServerEndpointsConfigurer:用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)。
    主要配置如下:

 

 

配置客户端详情信息(Client Details)

ClientDetailsServiceConfigurer 主要是注入ClientDetailsService实例对象(AuthorizationServerConfigurer 的一个回调配置项,唯一配置注入) 能够使用内存或者JDBC来实现客户端详情服务(ClientDetailsService),系统提供的二个ClientDetailsService实现类:JdbcClientDetailsService、InMemoryClientDetailsService。

Spring Security OAuth2的配置方法是编写@Configuration类继承AuthorizationServerConfigurerAdapter,然后重写void configure(ClientDetailsServiceConfigurer clients)方法,如:

回调配置ClientDetailsServiceConfigurer

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 使用JdbcClientDetailsService客户端详情服务
        clients.withClientDetails(new JdbcClientDetailsService(dataSource));
    }

这里使用Jdbc实现客户端详情服务,数据源dataSource不做叙述,使用框架默认的表,schema链接:
https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql

   /**
	 *
	 * 配置从哪里获取ClientDetails信息。
	 * 在client_credentials授权方式下,只要这个ClientDetails信息。
	 * @param clientsDetails
	 * @throws Exception
	 */
	@Override
	public void configure(ClientDetailsServiceConfigurer clientsDetails) throws Exception {
		//认证信息从数据库获取
		clientsDetails.withClientDetails(clientDetailsService);
		// 测试用,将客户端信息存储在内存中
		clientsDetails.inMemory()
				.withClient("client")   // client_id
				.secret("secret")       // client_secret
				.authorizedGrantTypes("authorization_code")     // 该client允许的授权类型
				.scopes("app")     // 允许的授权范围
				.autoApprove(true); //登录后绕过批准询问(/oauth/confirm_access)
			
	}

JdbcClientDetailsService类 

 
public class JdbcClientDetailsService implements ClientDetailsService, ClientRegistrationService {
 
    //操作oauth_client_details数据库SQL语句,另外可以自行注入
    private static final String CLIENT_FIELDS_FOR_UPDATE = "resource_ids, scope, "
            + "authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, "
            + "refresh_token_validity, additional_information, autoapprove";
    private static final String CLIENT_FIELDS = "client_secret, " + CLIENT_FIELDS_FOR_UPDATE;
    private static final String BASE_FIND_STATEMENT = "select client_id, " + CLIENT_FIELDS + " from oauth_client_details";
    private static final String DEFAULT_FIND_STATEMENT = BASE_FIND_STATEMENT + " order by client_id";
    private static final String DEFAULT_SELECT_STATEMENT = BASE_FIND_STATEMENT + " where client_id = ?";
    private static final String DEFAULT_INSERT_STATEMENT = "insert into oauth_client_details (" + CLIENT_FIELDS
            + ", client_id) values (?,?,?,?,?,?,?,?,?,?,?)";
    private static final String DEFAULT_UPDATE_STATEMENT = "update oauth_client_details " + "set "
            + CLIENT_FIELDS_FOR_UPDATE.replaceAll(", ", "=?, ") + "=? where client_id = ?";
    private static final String DEFAULT_UPDATE_SECRET_STATEMENT = "update oauth_client_details "
            + "set client_secret = ? where client_id = ?";
    private static final String DEFAULT_DELETE_STATEMENT = "delete from oauth_client_details where client_id = ?";
 
    private RowMapper<ClientDetails> rowMapper = new ClientDetailsRowMapper();
    //1.用于client_secret密码入库与出库时转化
    private PasswordEncoder passwordEncoder = NoOpPasswordEncoder.getInstance();
    //2.数据库存操作。
    private final JdbcTemplate jdbcTemplate;
    private JdbcListFactory listFactory;
 
    public JdbcClientDetailsService(DataSource dataSource) {
        Assert.notNull(dataSource, "DataSource required");
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        this.listFactory = new DefaultJdbcListFactory(new NamedParameterJdbcTemplate(jdbcTemplate));
    }
 
    /**
     * 核心方法。加载ClientDetails by clientId
     */
    public ClientDetails loadClientByClientId(String clientId) throws InvalidClientException {
        ClientDetails details;
        try {
            details = jdbcTemplate.queryForObject(selectClientDetailsSql, new ClientDetailsRowMapper(), clientId);
        }
        catch (EmptyResultDataAccessException e) {
            throw new NoSuchClientException("No client with requested id: " + clientId);
        }
        return details;
    }
 
}

InMemoryClientDetailsService类 

public class InMemoryClientDetailsService implements ClientDetailsService {
 
  private Map<String, ClientDetails> clientDetailsStore = new HashMap<String, ClientDetails>();
 
  public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
    ClientDetails details = clientDetailsStore.get(clientId);
    return details;
  }
}

 

AuthorizationServerEndpointsConfigurer端点配置

AuthorizationServerEndpointsConfigurer其实是一个装载类,装载Endpoints所有相关的类配置(AuthorizationServer、TokenServices、TokenStore、ClientDetailsService、UserDetailsService)。

    /**
	 * 注入相关配置:
	 * 1. 密码模式下配置认证管理器 AuthenticationManager
	 * 2. 设置 AccessToken的存储介质tokenStore, 默认使用内存当做存储介质。
      * 3. userDetailsService注入
	 */
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
 
		TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
		endpoints
				.authenticationManager(authenticationManager)
				//末确认点.userDetailsService(userDetailsService) //MUST:密码模式下需设置一个AuthenticationManager对象,获取 UserDetails信息
				.tokenStore(tokenStore)//token的保存方式
				.tokenEnhancer(tokenEnhancerChain);//token里加点信息
}

AuthorizationServerEndpointsConfigurer类: 

public final class AuthorizationServerEndpointsConfigurer {
 
	private AuthorizationServerTokenServices tokenServices;
	private ConsumerTokenServices consumerTokenServices;
	private AuthorizationCodeServices authorizationCodeServices;
	private ResourceServerTokenServices resourceTokenServices;
	private TokenStore tokenStore;
	private TokenEnhancer tokenEnhancer;
	private AccessTokenConverter accessTokenConverter;
	private ApprovalStore approvalStore;
	private TokenGranter tokenGranter;
	private OAuth2RequestFactory requestFactory;
	private OAuth2RequestValidator requestValidator;
	private UserApprovalHandler userApprovalHandler;
	private AuthenticationManager authenticationManager;
	private ClientDetailsService clientDetailsService;
    private String prefix;
	private Map<String, String> patternMap = new HashMap<String, String>();
	private Set<HttpMethod> allowedTokenEndpointRequestMethods = new HashSet<HttpMethod>();
 
	private FrameworkEndpointHandlerMapping frameworkEndpointHandlerMapping;
 
	private boolean approvalStoreDisabled;
	private List<Object> interceptors = new ArrayList<Object>();
 
	private DefaultTokenServices defaultTokenServices;
 
	private UserDetailsService userDetailsService;
 
	private boolean tokenServicesOverride = false;
 
	private boolean userDetailsServiceOverride = false;
 
	private boolean reuseRefreshToken = true;
 
	private WebResponseExceptionTranslator<OAuth2Exception> exceptionTranslator;
 
	private RedirectResolver redirectResolver;
 
/**
* tokenServices重新生成一个PreAuthenticatedAuthenticationProvider作为认证。
*/
private void addUserDetailsService(DefaultTokenServices tokenServices, UserDetailsService userDetailsService) {
		if (userDetailsService != null) {
			PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
			provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken>(
					userDetailsService));
			tokenServices
					.setAuthenticationManager(new ProviderManager(Arrays.<AuthenticationProvider> asList(provider)));
		}
	}
}

配置令牌 管理 (jwtAccessTokenConverter)

JwtAccessTokenConverter是用来生成token的转换器,而token令牌默认是有签名的,且资源服务器需要验证这个签名。此处的加密及验签包括两种方式:
对称加密、非对称加密(公钥密钥)
对称加密需要授权服务器和资源服务器存储同一key值,而非对称加密可使用密钥加密,暴露公钥给资源服务器验签,本文中使用非对称加密方式,配置于AuthorizationServerConfigurerAdapter如下:

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(authenticationManager)
                // 配置JwtAccessToken转换器
                .accessTokenConverter(jwtAccessTokenConverter())
                // refresh_token需要userDetailsService
                .reuseRefreshTokens(false).userDetailsService(userDetailsService);
                //.tokenStore(getJdbcTokenStore());
    }

    /**
     * 使用非对称加密算法来对Token进行签名
     * @return
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {

        final JwtAccessTokenConverter converter = new JwtAccessToken();
        // 导入证书
        KeyStoreKeyFactory keyStoreKeyFactory =
                new KeyStoreKeyFactory(new ClassPathResource("keystore.jks"), "mypass".toCharArray());
        converter.setKeyPair(keyStoreKeyFactory.getKeyPair("mytest"));

        return converter;
    }

通过 JDK 工具生成 JKS 证书文件,并将 keystore.jks 放入resource目录下

keytool -genkeypair -alias mytest -keyalg RSA -keypass mypass -keystore keystore.jks -storepass mypass


此处我们自定义JwtAccessToken用于添加额外用户信息

/**
 * Created by fp295 on 2018/4/16.
 * 自定义JwtAccessToken转换器
 */
public class JwtAccessToken extends JwtAccessTokenConverter {

    /**
     * 生成token
     * @param accessToken
     * @param authentication
     * @return
     */
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        DefaultOAuth2AccessToken defaultOAuth2AccessToken = new DefaultOAuth2AccessToken(accessToken);

        // 设置额外用户信息
        BaseUser baseUser = ((BaseUserDetail) authentication.getPrincipal()).getBaseUser();
        baseUser.setPassword(null);
        // 将用户信息添加到token额外信息中
        defaultOAuth2AccessToken.getAdditionalInformation().put(Constant.USER_INFO, baseUser);

        return super.enhance(defaultOAuth2AccessToken, authentication);
    }

    /**
     * 解析token
     * @param value
     * @param map
     * @return
     */
    @Override
    public OAuth2AccessToken extractAccessToken(String value, Map<String, ?> map){
        OAuth2AccessToken oauth2AccessToken = super.extractAccessToken(value, map);
        convertData(oauth2AccessToken, oauth2AccessToken.getAdditionalInformation());
        return oauth2AccessToken;
    }

    private void convertData(OAuth2AccessToken accessToken,  Map<String, ?> map) {
        accessToken.getAdditionalInformation().put(Constant.USER_INFO,convertUserData(map.get(Constant.USER_INFO)));

    }

    private BaseUser convertUserData(Object map) {
        String json = JsonUtils.deserializer(map);
        BaseUser user = JsonUtils.serializable(json, BaseUser.class);
        return user;
    }
}

JwtAccessToken 类中从authentication里的getPrincipal(实际为UserDetails接口)获取用户信息,所以我们需要实现自己的UserDetails

/**
 * Created by fp295 on 2018/4/29.
 * 包装org.springframework.security.core.userdetails.User类
 */
public class BaseUserDetail implements UserDetails, CredentialsContainer {

    private final BaseUser baseUser;
    private final org.springframework.security.core.userdetails.User user;

    public BaseUserDetail(BaseUser baseUser, User user) {
        this.baseUser = baseUser;
        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 BaseUser getBaseUser() {
        return baseUser;
    }
}

AuthenticationManager

AuthenticationManager是一个用来处理认证(Authentication)请求的接口。在其中只定义了一个方法authenticate(),该方法只接收一个代表认证请求的Authentication对象作为参数,如果认证成功,则会返回一个封装了当前用户权限等信息的Authentication对象进行返回。

Authentication authenticate(Authentication authentication) throws AuthenticationException;


在Spring Security中,AuthenticationManager的默认实现是ProviderManager,而且它不直接自己处理认证请求,而是委托给其所配置的AuthenticationProvider列表,然后会依次使用每一个AuthenticationProvider进行认证,如果有一个AuthenticationProvider认证后的结果不为null,则表示该AuthenticationProvider已经认证成功,之后的AuthenticationProvider将不再继续认证。然后直接以该AuthenticationProvider的认证结果作为ProviderManager的认证结果。如果所有的AuthenticationProvider的认证结果都为null,则表示认证失败,将抛出一个ProviderNotFoundException。
校验认证请求最常用的方法是根据请求的用户名加载对应的UserDetails,然后比对UserDetails的密码与认证请求的密码是否一致,一致则表示认证通过。
Spring Security内部的DaoAuthenticationProvider就是使用的这种方式。其内部使用UserDetailsService来负责加载UserDetails。在认证成功以后会使用加载的UserDetails来封装要返回的Authentication对象,加载的UserDetails对象是包含用户权限等信息的。认证成功返回的Authentication对象将会保存在当前的SecurityContext中。

实现UserDetailsService

UserDetailsService只定义了一个方法 loadUserByUsername,根据用户名可以查到用户并返回的方法。

@Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        logger.debug("权限框架-加载用户");
        List<GrantedAuthority> auths = new ArrayList<>();

        BaseUser baseUser = new BaseUser();
        baseUser.setUserName(username);
        baseUser = baseUserService.selectOne(baseUser);

        if (baseUser == null) {
            logger.debug("找不到该用户 用户名:{}", username);
            throw new UsernameNotFoundException("找不到该用户!");
        }
        if(baseUser.getStatus()==2)
        {
            logger.debug("用户被禁用,无法登陆 用户名:{}", username);
            throw new UsernameNotFoundException("用户被禁用!");
        }
        List<BaseRole> roles = baseRoleService.selectRolesByUserId(baseUser.getId());
        if (roles != null) {
            //设置角色名称
            for (BaseRole role : roles) {
                SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.getRoleCode());
                auths.add(authority);
            }
        }

        return new org.springframework.security.core.userdetails.User(baseUser.getUserName(), baseUser.getUserPassword(), true, true, true, true, auths);
    }

 

AuthorizationServerSecurityConfigurer端点安全配置:  

 AuthorizationServerSecurityConfigurer继承SecurityConfigurerAdapter.也就是一个 Spring Security安全配置提供给AuthorizationServer去配置AuthorizationServer的端点(/oauth/****)的安全访问规则、过滤器Filter。

类继承关系:

可配置属性项: 

1. ClientDetail加密方式

2. allowFormAuthenticationForClients 允许表单认证。针对/oauth/token端点。

3. 添加开发配置tokenEndpointAuthenticationFilters

4. tokenKeyAccess、checkTokenAccess访问权限。

/**
	 *  配置:安全检查流程,用来配置令牌端点(Token Endpoint)的安全与权限访问
	 *  默认过滤器:BasicAuthenticationFilter
	 *  1、oauth_client_details表中clientSecret字段加密【ClientDetails属性secret】
	 *  2、CheckEndpoint类的接口 oauth/check_token 无需经过过滤器过滤,默认值:denyAll()
	 * 对以下的几个端点进行权限配置:
	 * /oauth/authorize:授权端点
	 * /oauth/token:令牌端点
	 * /oauth/confirm_access:用户确认授权提交端点
	 * /oauth/error:授权服务错误信息端点
	 * /oauth/check_token:用于资源服务访问的令牌解析端点
	 * /oauth/token_key:提供公有密匙的端点,如果使用JWT令牌的话
	 **/
	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
		security.allowFormAuthenticationForClients()//允许客户表单认证
				.passwordEncoder(new BCryptPasswordEncoder())//设置oauth_client_details中的密码编码器
				.tokenKeyAccess("permitAll()")
				.checkTokenAccess("isAuthenticated()")
				.passwordEncoder(oauthClientPasswordEncoder);
	}
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
        oauthServer
                // 开启/oauth/token_key验证端口无权限访问
                .tokenKeyAccess("permitAll()")
                // 开启/oauth/check_token验证端口认证权限访问
                .checkTokenAccess("isAuthenticated()");
    }

AuthorizationServerSecurityConfigurer类

public final class AuthorizationServerSecurityConfigurer extends
        SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
 
    private AuthenticationEntryPoint authenticationEntryPoint;
    private AccessDeniedHandler accessDeniedHandler = new OAuth2AccessDeniedHandler();
    //client secrets加密器
    private PasswordEncoder passwordEncoder;
    private String realm = "oauth2/client";
    private boolean allowFormAuthenticationForClients = false;
    private String tokenKeyAccess = "denyAll()";
    private String checkTokenAccess = "denyAll()";
    private boolean sslOnly = false;
    //开发定义过滤器
    private List<Filter> tokenEndpointAuthenticationFilters = new ArrayList<Filter>();
 
 
    @Override
    public void init(HttpSecurity http) throws Exception {
        //1.异常发生时的入口配置
        registerDefaultAuthenticationEntryPoint(http);
        //1. passwordEncoder注入到ClientDetailsUserDetailsService(
        // UserDetailsService对象存储在HttpSecurity.SharedObject里。
        if (passwordEncoder != null) {
            ClientDetailsUserDetailsService clientDetailsUserDetailsService = new ClientDetailsUserDetailsService(clientDetailsService());
            clientDetailsUserDetailsService.setPasswordEncoder(passwordEncoder());
            http.getSharedObject(AuthenticationManagerBuilder.class)
                    .userDetailsService(clientDetailsUserDetailsService)
                    .passwordEncoder(passwordEncoder());
        }
        else {
            http.userDetailsService(new ClientDetailsUserDetailsService(clientDetailsService()));
        }
        //2.配置/oaut/***端点 httpBasic安全规则
        http.securityContext().securityContextRepository(new NullSecurityContextRepository()).and().csrf().disable()
                .httpBasic().realmName(realm);
        //3. ssl 通道安全
        if (sslOnly) {
            http.requiresChannel().anyRequest().requiresSecure();
        }
    }
 
    /**
     * 开发配置:
     * 1. allowFormAuthenticationForClients 允许表单认证。针对/oauth/token端点添加ClientCredentialsTokenEndpointFilter
     * 2. 添加开发配置tokenEndpointAuthenticationFilters。(在 BasicAuthenticationFilter之前)
     * 3. 添加在 accessDeniedHandler
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
 
        // ensure this is initialized
        frameworkEndpointHandlerMapping();
        //2. 针对/oauth/token端点添加ClientCredentialsTokenEndpointFilter
        if (allowFormAuthenticationForClients) {
            clientCredentialsTokenEndpointFilter(http);
        }
        //3.添加开发配置tokenEndpointAuthenticationFilters。(在 BasicAuthenticationFilter之前)
        for (Filter filter : tokenEndpointAuthenticationFilters) {
            http.addFilterBefore(filter, BasicAuthenticationFilter.class);
        }
        //4. 添加在 accessDeniedHandler,处理访问AccessDeniedException发生时处理。
        http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
    }
 
    /**
     * 配置异常发生时入口点AuthenticationEntryPoint
     * @param http
     */
    private void registerDefaultAuthenticationEntryPoint(HttpSecurity http) {
        ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);
        if (exceptionHandling == null) {
            return;
        }
        //1.端点入口:BasicAuthenticationEntryPoint设置response配置。
        // response.addHeader("WWW-Authenticate", "Basic realm=\"" + realmName + "\"");
        // response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
        if (authenticationEntryPoint==null) {
            BasicAuthenticationEntryPoint basicEntryPoint = new BasicAuthenticationEntryPoint();
            basicEntryPoint.setRealmName(realm);
            authenticationEntryPoint = basicEntryPoint;
        }
        //发生异常时,会调用到BasicAuthenticationEntryPoint.commence()
        exceptionHandling.defaultAuthenticationEntryPointFor(postProcess(authenticationEntryPoint), preferredMatcher);
    }
 
    /**
     * 核心:
     * "/oauth/token"端点的添加过滤器clientCredentialsTokenEndpointFilter(clientId, client_secert)密码比对。而且
     *  放到BasicAuthenticationFilter之前。
     *  http.addFilterBefore(clientCredentialsTokenEndpointFilter, BasicAuthenticationFilter.class);
     */
    private ClientCredentialsTokenEndpointFilter clientCredentialsTokenEndpointFilter(HttpSecurity http) {
        ClientCredentialsTokenEndpointFilter clientCredentialsTokenEndpointFilter = new ClientCredentialsTokenEndpointFilter(
                frameworkEndpointHandlerMapping().getServletPath("/oauth/token"));
        clientCredentialsTokenEndpointFilter
                .setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
        OAuth2AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
        authenticationEntryPoint.setTypeName("Form");
        authenticationEntryPoint.setRealmName(realm);
        clientCredentialsTokenEndpointFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
        clientCredentialsTokenEndpointFilter = postProcess(clientCredentialsTokenEndpointFilter);
        http.addFilterBefore(clientCredentialsTokenEndpointFilter, BasicAuthenticationFilter.class);
        return clientCredentialsTokenEndpointFilter;
    }
 
    /**
     * 居然使用共享方式
     */
    private ClientDetailsService clientDetailsService() {
        return getBuilder().getSharedObject(ClientDetailsService.class);
    }
 
    private FrameworkEndpointHandlerMapping frameworkEndpointHandlerMapping() {
        return getBuilder().getSharedObject(FrameworkEndpointHandlerMapping.class);
    }
 
    private PasswordEncoder passwordEncoder() {
        return new PasswordEncoder() {
            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                return StringUtils.hasText(encodedPassword) ? passwordEncoder.matches(rawPassword, encodedPassword)
                        : true;
            }
            @Override
            public String encode(CharSequence rawPassword) {
                return passwordEncoder.encode(rawPassword);
            }
        };
    }
 
}
public class ClientCredentialsTokenEndpointFilter extends AbstractAuthenticationProcessingFilter {
 
	private AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
 
    public ClientCredentialsTokenEndpointFilter() {
		this("/oauth/token");
	}
 
	public ClientCredentialsTokenEndpointFilter(String path) {
		super(path);
		setRequiresAuthenticationRequestMatcher(new ClientCredentialsRequestMatcher(path));
		// If authentication fails the type is "Form"
		((OAuth2AuthenticationEntryPoint) authenticationEntryPoint).setTypeName("Form");
	}
 
	@Override
	public void afterPropertiesSet() {
		super.afterPropertiesSet();
		setAuthenticationFailureHandler(new AuthenticationFailureHandler() {
			public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
					AuthenticationException exception) throws IOException, ServletException {
				if (exception instanceof BadCredentialsException) {
					exception = new BadCredentialsException(exception.getMessage(), new BadClientCredentialsException());
				}
				authenticationEntryPoint.commence(request, response, exception);
			}
		});
		setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
			public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
					Authentication authentication) throws IOException, ServletException {
				// no-op - just allow filter chain to continue to token endpoint
			}
		});
	}
 
	@Override
	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
			throws AuthenticationException, IOException, ServletException {
        //1.获取请求参数:clientId、clientSecret用来认证
		String clientId = request.getParameter("client_id");
		String clientSecret = request.getParameter("client_secret");
 
		//2.如果认证过就不需要再认证
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		if (authentication != null && authentication.isAuthenticated()) {
			return authentication;
		}
		if (clientId == null) {
			throw new BadCredentialsException("No client credentials presented");
		}
		if (clientSecret == null) {
			clientSecret = "";
		}
 
		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(clientId,
				clientSecret);
        //4.开始认证
		return this.getAuthenticationManager().authenticate(authRequest);
 
	}

 

 

  • 10
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值