OAuth2协议 白话理解+SpringCloud Security 栗子

引言

OAuth2.0OAuth协议的延续版本,但不向前兼容OAuth 1.0(即完全废止了OAuth1.0)。 OAuth 2.0关注客户端开发者的简易性。要么通过组织在资源拥有者和HTTP服务商之间的被批准的交互动作代表用户,要么允许第三方应用代表用户获得访问的权限。同时为Web应用,桌面应用和手机,和起居室设备提供专门的认证流程。2012年10月,OAuth 2.0协议正式发布为RFC 6749。【百度百科】

微服务架构中,我们通常使用无状态形式存储用户信息,比如JWT,那么我们就想开发一个单独的认证授权服务出来,此时我们还要考虑到这个认证授权服务是在微服务架构中产生的,那么我们肯定要考虑到这个认证授权服务能授权我们访问哪些服务呢?

白话理解专业名词

认证授权服务和资源服务

针对上面那个问题,就出现了认证授权服务资源服务,所谓资源服务就是授权后我们能被访问的那些服务,白话理解就是说资源服务可以是订单服务,充值服务等等,那么为什么需要定义资源服务呢?

此时你是不是在想,既然认证通过,那么理应可以访问所有的服务,就像单体应用那样,认证通过,只需要验证角色就行了。

这要说到最近比较火的词,中台,这和中台的理念极为相近,无论前台后台,都直接调用这里的接口即可。如果你不了解这个词,没关系,我这里有白话解释。
比如,我这个公司,开发了很多个APP每个APP对应了不同的服务,比如:快买啊APP只能访问订单服务充值服务理财APP可以访问充值服务提现服务购买理财产品服务,虽然这些APP可以共用账户的认证信息,但我需要区分每个client对应的resources有哪些。又或者我授权第三方某个APP可以访问我的某些服务,此时这个APP就是client资源,它其中对应的resources就是可以访问哪些我的服务,此时我们将授权粒度分的更细了。

现在我们明白了,认证授权服务它就只提供认证和授权,其中clients对应的就是一个个APPresources对应的就是各个服务,我们在数据库中存着的信息就是每个client包含哪些resourcesscopes,关于scopes我决定下面单说。

scope作用域

在上面对客户端进行资源区分后,还有一个东西,scope(作用域),这玩意的作用会将资源近一步的区分,在创建clients对应关系时,还会添加一个scopes,其意义在于我明确表示这个APP(client)访问资源服务(resource)的时候的作用域(scope)
比如:虽然快买啊APP理财APP都能访问充值服务,但是我可以规定充值服务银行卡充值只能由理财APP访问,此时,我在充值服务中,将银行卡充值方法加了一个判断"#oauth2.hasScope(\"licai\")"作用域同理可以限制第三方APP

关于白话理解

无论我写的多白话,实际上你看的应该还是有点云里雾里的,因为这玩意确实挺绕的,所以下面我会给一个实际例子,理论+代码=Get。

搭建认证授权服务

引入依赖

		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>

编写认证配置(SecurityConfig)

实际上这就是你在springboot单体应用中security的配置。
它只负责认证账号密码

package com.doub1.authorizationserver.config;

import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowCredentials(true);
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setMaxAge(18000L);
        source.registerCorsConfiguration("/**", corsConfiguration);
        http.authorizeRequests().anyRequest().permitAll()
                .and().csrf().disable().formLogin().and()
                .cors().configurationSource(source).and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
    }
    
    //为了省事 我没有写UserDetailsService
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("test").password(bCryptPasswordEncoder().encode("test123")).authorities("admin")
                .and().passwordEncoder(bCryptPasswordEncoder());
    }
}

配置授权服务

其中需要配置的,ClientDetailsServiceTokenStoreAuthorizationServerTokenServices等,详细信息我写在了注释内。

基于内存Token的授权服务配置

缺点:资源服务需要通过RemoteTokenService,转发Token,请求让我们验证。

package com.doub1.authorizationserver.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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.configuration.EnableAuthorizationServer;
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.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;


@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
    public OAuth2Config() {
        super();
    }

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

	//token 存储方式
	//如果是内存,那么资源服务自己就无法验证信息
	//因为token信息是存在我们的内存里
	//资源服务那边定义TokenService的时候就会使用RemoteTokenService,通过我们来验证Token.
    @Bean
    TokenStore tokenStore(){
       return new InMemoryTokenStore();
    }

	//可以自定义客户端信息服务 ClientDetailsService,我这里是测试栗子,可以使用内存方式创建
    @Autowired
    private ClientDetailsService clientDetailsService;

	//配置token服务
    @Bean
    AuthorizationServerTokenServices tokenService(){
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        //设置ClientDetailsService
        defaultTokenServices.setClientDetailsService(clientDetailsService);
        //是否支持刷新token
        defaultTokenServices.setSupportRefreshToken(true);
        //设置token存储方式
        defaultTokenServices.setTokenStore(tokenStore());
        //设置token有效期
        defaultTokenServices.setAccessTokenValiditySeconds(7200);
        //设置token刷新的有效期(在时间范围内都可以刷新,哪怕token已经失效)
        defaultTokenServices.setRefreshTokenValiditySeconds(7200*2);
        return defaultTokenServices;
    }

	//配置ClientDetailsService,内存方式
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    	//客户端只有一个myApp,能访问的资源有a-service和b-service
    	//授权允许授权码,用户密码,客户端凭证,显式授权,允许刷新token
    	//并且myApp申请token后重定向地址为"/"绝对路径
        clients.inMemory()
                .withClient("myApp")
                .secret(bCryptPasswordEncoder.encode("secret"))
                .resourceIds("a-service","b-service")
                .authorizedGrantTypes("authorization_code","password","client_credentials","implicit","refresh_token")
                .scopes("all")
                .autoApprove(false)
                .redirectUris("/");
    }

	//配置安全项
	//允许创建token和检查token
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("permitAll()")
                .passwordEncoder(bCryptPasswordEncoder)
                .allowFormAuthenticationForClients();
    }

    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
                //.authorizationCodeServices(authorizationCodeServices())
                /* 为空的时候会自动获取InMemoryAuthorizationCodeServices
                * if (authorizationCodeServices == null) {
                    authorizationCodeServices = new InMemoryAuthorizationCodeServices();
                    }
                    return authorizationCodeServices;
                * */
                .tokenServices(tokenService())
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    }
}

基于JwtToken的授权服务配置

无状态的,验证Token只要确保资源服务也使用相同的加密算法就可以校验。需要注意的是,认证配置那里需要将sessionCreationPolicy改为无状态的,因为我们不需要存储。

package com.doub1.authorizationserver.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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.configuration.EnableAuthorizationServer;
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.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;


@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
    public OAuth2Config() {
        super();
    }

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

	@Bean
    JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey("SIGNATURE_KEY");
        return jwtAccessTokenConverter;
    }

	//token 存储方式
	//我们使用JWT转换器,将其设置为无状态的
    @Bean
    TokenStore tokenStore(){
       //return new InMemoryTokenStore();
       return new JwtTokenStore(jwtAccessTokenConverter());
    }
	
 

	//可以自定义客户端信息服务 ClientDetailsService,我这里是测试栗子,可以使用内存方式创建
    @Autowired
    private ClientDetailsService clientDetailsService;

	//配置token服务
     @Bean
    AuthorizationServerTokenServices tokenService(){
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setClientDetailsService(clientDetailsService);
        defaultTokenServices.setSupportRefreshToken(true);
        defaultTokenServices.setTokenStore(tokenStore());//TokenStore

		//Token增强器链
        TokenEnhancerChain tokenEnhancerChain=new TokenEnhancerChain();
        //jwtAccessTokenConverter本身就继承TokenEnhancer,我们在链条后加了一个TokenEnhancer,让其在JWT中加入一些我们想加入的信息
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter(), new TokenEnhancer() {
            @Override
            public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
                Map<String, Object> additionalInformation = accessToken.getAdditionalInformation();
                System.out.println(additionalInformation);
                additionalInformation.put("自定义信息","蛇皮哟");
                ((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(additionalInformation);
                return accessToken;
            }
        }));
        defaultTokenServices.setTokenEnhancer(tokenEnhancerChain);


        defaultTokenServices.setAccessTokenValiditySeconds(7200);
        defaultTokenServices.setRefreshTokenValiditySeconds(7200*2);
        return defaultTokenServices;
    }

	//配置ClientDetailsService,内存方式
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    	//客户端只有一个myApp,能访问的资源有a-service和b-service
    	//授权允许授权码,用户密码,客户端凭证,显式授权,允许刷新token
    	//并且myApp申请token后重定向地址为"/"绝对路径
        clients.inMemory()
                .withClient("myApp")
                .secret(bCryptPasswordEncoder.encode("secret"))
                .resourceIds("a-service","b-service")
                .authorizedGrantTypes("authorization_code","password","client_credentials","implicit","refresh_token")
                .scopes("all")
                .autoApprove(false)
                .redirectUris("/");
    }

	//配置安全项
	//允许创建token和检查token
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("permitAll()")
                .passwordEncoder(bCryptPasswordEncoder)
                .allowFormAuthenticationForClients();
    }

    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
                //.authorizationCodeServices(authorizationCodeServices())
                /* 为空的时候会自动获取InMemoryAuthorizationCodeServices
                * if (authorizationCodeServices == null) {
                    authorizationCodeServices = new InMemoryAuthorizationCodeServices();
                    }
                    return authorizationCodeServices;
                * */
                .tokenServices(tokenService())
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    }
}

申请token

http://localhost:8080/oauth/gateway重定向到的认证授权服务,也就是上面那个栗子。路径你们自己看着改。

简化模式申请token

response_type改为token即可

http://localhost:8080/oauth/authorize?client_id=myApp&response_type=token&scope=all&redirect_uri=/

申请授权码

这种方式用在访问公共服务,因为token中并没有用户信息,比如:游客状态。

http://localhost:8080/oauth/authorize?client_id=myApp&response_type=code&scope=all&redirect_uri=/

根据授权码申请token

根据返回的code 申请token

http://localhost:8080/oauth/token?client_id=myApp&client_secret=secret&grant_type=authorization_code&code={申请到的授权码}&redirect_uri=/

密码模式申请token

http://localhost:8080/oauth/token?client_id=myApp&client_secret=secret&grant_type=password&username=test&password=test123&redirect_uri=/

刷新token

http://localhost:8080/oauth/token?client_id=myApp&grant_type=refresh_toekn&refresh_token={需要刷新的token}&client_secret=secret&redirect_uri=/

检查token

http://localhost:8080/oauth/check_token?client_id=myApp&client_secret=secret&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYi1zZXJ2aWNlIiwiYS1zZXJ2aWNlIl0sInVzZXJfbmFtZSI6InRlc3QiLCJzY29wZSI6WyJhbGwiXSwiZXhwIjoxNTkwMjkxNTczLCJhdXRob3JpdGllcyI6WyJhZG1pbiJdLCJqdGkiOiI2OWE4YjVjZi1iNzE5LTRkZDQtOWZkZS0xNTc0Yzg1ZGFlYjIiLCJjbGllbnRfaWQiOiJteUFwcCJ9.WuIWN3HpxdUzZdiiH1lwat7700XEz75HapFTxGhr9FA

搭建资源服务

引入依赖

		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>

配置资源服务

资源服务就是以前的订单服务,充值服务等,我们是需要加上鉴权而已,我这里就是里面的a-service.

远程调用认证授权服务的TokenService资源验证

package com.doub1.aservice.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Value("${spring.application.name}")
    private String APPLICATION_NAME;

    @Autowired
    private RestTemplate restTemplate;

	//远程token服务,这样会将token验证转发到认证授权服务去验证
    @Bean
    RemoteTokenServices tokenServices(){
        RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
        remoteTokenServices.setRestTemplate(restTemplate);
        remoteTokenServices.setCheckTokenEndpointUrl("http://authorization-server/oauth/check_token");
        remoteTokenServices.setClientId("myApp");
        remoteTokenServices.setClientSecret("secret");
        return remoteTokenServices;
    }

	//指定自己的resourceID,我们的APPLICATION_NAME就是a-service
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId(APPLICATION_NAME).tokenServices(tokenServices()).stateless(true);
    }

	//鉴权配置,访问是否具有权限
    @Override
    public void configure(HttpSecurity http) throws Exception {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowCredentials(true);
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setMaxAge(18000L);
        source.registerCorsConfiguration("/**", corsConfiguration);

        http.authorizeRequests()
                .anyRequest().access("#oauth2.hasScope(\"all\")")
                .and().csrf().disable().cors().configurationSource(source)
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
}

基于JWT的无状态资源验证

package com.doub1.aservice.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Value("${spring.application.name}")
    private String APPLICATION_NAME;

    @Autowired
    private RestTemplate restTemplate;

	 @Bean
    JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey("SIGNATURE_KEY");
        return jwtAccessTokenConverter;
    }

    @Bean
    TokenStore tokenStore() {
        //return new InMemoryTokenStore();
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

	//指定自己的resourceID,我们的APPLICATION_NAME就是a-service
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        //这里的tokenServices直接换成tokenStore
        //我还贴心的为你们准备了自定义验证失败的处理类
        resources.resourceId(APPLICATION_NAME).tokenStore(tokenStore()).stateless(true)
        .authenticationEntryPoint(new AuthExceptionEntryPoint()).accessDeniedHandler(new CustomAccessDeniedHandler());
        //resources.resourceId(APPLICATION_NAME).tokenServices(tokenServices()).stateless(true);
    }

	//鉴权配置,访问是否具有权限
    @Override
    public void configure(HttpSecurity http) throws Exception {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowCredentials(true);
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setMaxAge(18000L);
        source.registerCorsConfiguration("/**", corsConfiguration);

        http.authorizeRequests()
                .anyRequest().access("#oauth2.hasScope(\"all\")")
                .and().csrf().disable().cors().configurationSource(source)
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
}

自定义验证失败处理

认证异常

package com.doub1.aservice.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class AuthExceptionEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)throws ServletException {
        Map map = new HashMap();
        map.put("error", "401");
        map.put("message", authException.getMessage());
        map.put("path", request.getServletPath());
        map.put("timestamp", String.valueOf(new Date().getTime()));
        response.setContentType("application/json");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        try {
            ObjectMapper mapper = new ObjectMapper();
            mapper.writeValue(response.getOutputStream(), map);
        } catch (Exception e) {
            throw new ServletException();
        }
    }
}

拒绝访问

package com.doub1.aservice.config;


import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component("customAccessDeniedHandler")
public class CustomAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        response.setContentType("application/json;charset=UTF-8");
        Map map = new HashMap();
        map.put("error", "400");
        map.put("message", accessDeniedException.getMessage());
        map.put("path", request.getServletPath());
        map.put("timestamp", String.valueOf(new Date().getTime()));
        response.setContentType("application/json");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.getWriter().write(objectMapper.writeValueAsString(map));
    }
}

资源服务中如何获取用户信息

SecurityContextHolder的上下文中存着认证信息,获取也非常方便。

@RestController
public class HomeController {
    @RequestMapping("/")
    @PreAuthorize("hasAuthority(\"admin\")")
    public String index(){
    	//SecurityContextHolder 直接就能拿到认证服务那边的信息了。是不是很简单
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return authentication.getName();
    }
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

没事干写博客玩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值