什么?github、支付宝、微信都在用的OAuth2你没还实


什么?github、支付宝、微信都在用的OAuth2你都没还实战过吗?今天主要讲解一下登陆鉴权的流程,包括涉及到的源码部分,当时也踩了很多的坑,通过记录的方式,自己分析了一下整个流程,有需要源码的可找我哦!

1.基于OAuth 2.0+JWT+spring security完成认证授权

首先分析一下为什么要用OAuth2和JWT来做

  1. 单点登录(SSO):方案单击登录方案是最常见的解决方案,但单点登录需要每个与用户交互的服务都必须与认证服务进行通信,这不但会造成重复,也会产生大量琐碎的网络流量
  2. 分布式会话(Session):方案通过将用户会话信息存储在共享存储中,如Redis,并使用用户会话的ID作为key来实现分布式哈希映射。当用户访问微服务时,会话数据就可以从共享存储中获取。该解决方案在高可用和扩展方面都很好,但是由于会话信息保存在共享存储中,所以需要一定的保护机制保护数据安全,因此在具体的实现中会具有比较高的复杂度
  3. 客户端令牌(Token):方案令牌由客户端生成,并由认证服务器签名。在令牌中会包含足够的信息,客户端在请求时会将令牌附加在请求上,从而为各个微服务提供用户身份数据。此方案解决了分布式会话方案的安全性问题,但如何及时注销用户认证信息则是一个大问题,虽然可以使用短期令牌并频繁地与认证服务器进行校验,但并不可以彻底解决。JWT(JSON Web Tokens)是非常出名的客户端令牌解决方案,它足够简单,并且对各种环境支持程度也比较高
  4. 客户端令牌与API网关结合

通过在微服务架构中实施API网关,可以将原始的客户端令牌转换为内部会话令牌。一方面可以有效地隐藏微服务,另一方面通过API网关的统一入口可以实现令牌的注销处理。在David Borsos的第二个方案:分布式Session方案中要求开发者能够将用户会话信息单独拎出来进行集中管理。业界比较成熟的开源项目有Spring Session,其使用Redis数据库或缓存机制来实现Session存储,并通过过滤器实现Session数据的自动加载。随着近几年云服务应用的发展,基于令牌(Token)的认证使用范围也越来越广。对于基于令牌认证通常包含下面几层含义:

  • 令牌是认证用户信息的集合,而不仅仅是一个无意义的ID。
  • 在令牌中已经包含足够多的信息,验证令牌就可以完成用户身份的校验,从而减轻了因为用户验证需要检索数据库的压力,提升了系统性能。
  • 因为令牌是需要服务器进行签名发放的,所以如果令牌通过解码认证,我们就可以认为该令牌所包含的信息是合法有效的。
  • 服务器会通过HTTP头部中的Authorization获取令牌信息并进行检查,并不需要在服务器端存储任何信息。
  • 通过服务器对令牌的检查机制,可以将基于令牌的认证使用在基于浏览器的客户端和移动设备的App或是第三方应用上。
  • 可以支持跨程序调用。基于Cookie是不允许垮域访问的,而令牌则不存在这个问题。

综上所述,基于令牌的认证由于会包含认证用户的相关信息,因此可以通过验证令牌来完成用户身份的校验,完全不同于之前基于会话的认证。因此,基于令牌的这个优点,像T微信、支付宝、微博及GitHub等,都推出了基于令牌的认证服务,用于访问所开放的API及单点登录。接下来将重点介绍基于令牌认证方案中的OAuth 2.0和JWT

2.两部分:认证服务端(认证及生成token) 、认证资源服务端(访问其他服务内的资源需要校验)

rs.png

3.先看看 客户端授权模式(一般采用授权码模式,简单来说就是你要重定向url认证服务器获取授权码(code),在获取访问令牌。

在上面流程图中第一步之后,会重定向到类似于

http://localhost:8080/token/oauth/authorize?client_id=client1&response_type=code&redirect_uri=/token
复制代码

会返回一个code
在访问

http://localhost:8080/oauth/token?client_id=client1&grant_type=authorization_code&redirect_uri=/token&code=
复制代码

附加上code值,这时就会返回access_token

还有一个问题:上面url的请求的路径需要保存在数据库中,需要新建一个表,固定的字段

image.png

CREATE TABLE `oauth_client_details` (
  `client_id` varchar(255) NOT NULL,
  `resource_ids` varchar(255) DEFAULT NULL,
  `client_secret` varchar(255) DEFAULT NULL,
  `scope` varchar(255) DEFAULT NULL,
  `authorized_grant_types` varchar(255) DEFAULT NULL,
  `web_server_redirect_uri` varchar(255) DEFAULT NULL,
  `authorities` varchar(255) DEFAULT NULL,
  `access_token_validity` int(11) DEFAULT NULL,
  `refresh_token_validity` int(11) DEFAULT NULL,
  `additional_information` varchar(4096) DEFAULT NULL,
  `autoapprove` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`client_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
复制代码

数据库保存授权用户

CREATE TABLE `sys_user` (
  `id` varchar(150) NOT NULL,
  `phone` varchar(50) DEFAULT NULL,
  `email` varchar(100) DEFAULT NULL,
  `password` varchar(150) NOT NULL,
  `disable` int(11) NOT NULL,
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `ip` varchar(150) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
复制代码

详情见如后代码

4.正式实战 (认证服务端)

pom.xml文件

        <dependency>
			<groupId>org.springframework.security.oauth</groupId>
			<artifactId>spring-security-oauth2</artifactId>
			<version>2.3.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.security.oauth.boot</groupId>
			<artifactId>spring-security-oauth2-autoconfigure</artifactId>
		</dependency>
复制代码

image.png
主要讲解这三个类,他们的的执行顺序:SecurityConfiguration->MyAuthenticationSuccessHandler->AuthorizationServerConfiguration

首先在用户中心登陆操作,获取账号密码,发送HTTP请求

private JSONObject requestToken(String account, String password, String deviceType) {
        String result = null;
        try {
            RestTemplate restTemplate = new RestTemplate();
            HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
            HttpClient httpClient = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build();
            factory.setHttpClient(httpClient);
            restTemplate.setRequestFactory(factory);

            MultiValueMap<String, Object> sendMap = new LinkedMultiValueMap<>();
            sendMap.add("username", account);
            sendMap.add("password", password);
            result = RestTemplateUtil.postForEntityFormData(restTemplate, Datas.AUTH_LOGIN_URL, sendMap, deviceType);
            logger.info("认证中心返回结果-------》》》》》" + result);
        } catch (Exception e) {
            logger.error("error", e);
            throw new Exception("500", e);
        }
        return JSON.parseObject(result);
    }
复制代码

方法执行顺序:userDetailsService(会获取到传进来的username)->protected void configure(HttpSecurity http)
->这里会执行config里的myAuthenticationFailureHandler->(这是第二个类)

import javax.transaction.Transactional;

import org.bifu.distributed.auth.constant.AuthContants;
import org.bifu.distributed.auth.dao.UserMapper;
import org.bifu.distributed.auth.domain.User;
import org.bifu.distributed.auth.dto.SecurityUserDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.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.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
	
	private final static Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);

	@Autowired
	private UserMapper userMapper;

	@Autowired
	private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;

	@Autowired
	private MyAuthenticationFailureHandler myAuthenticationFailureHandler;

	@Autowired
	public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
		// 配置用户来源于数据库
//		auth.userDetailsService(userDetailsService()).passwordEncoder(new MyPasswordEncoder());
		auth.userDetailsService(userDetailsService()).passwordEncoder(new BCryptPasswordEncoder());
//		auth.userDetailsService(userDetailsService()).passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());
//		auth.userDetailsService(userDetailsService()).passwordEncoder(MyPasswordEncoderFactories.createDelegatingPasswordEncoder());
	}

	/**
	 * authorizeRequests()配置路径拦截,表明路径访问所对应的权限,角色,认证信息。
	 * formLogin()对应表单认证相关的配置
	 * logout()对应了注销相关的配置
	 * httpBasic()可以配置basic登录
	 * @param http
	 * @throws Exception
	 */
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.formLogin().loginProcessingUrl("/auth/login").successHandler(myAuthenticationSuccessHandler)
				.failureHandler(myAuthenticationFailureHandler).and().csrf().disable().sessionManagement()
				.maximumSessions(1).expiredUrl("/expiredSession");
		logger.info("test是否生成token");
	}

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

	@Override
	@Bean
	public UserDetailsService userDetailsService() {
		return new UserDetailsService() {
			@Override
			@Transactional(rollbackOn = Exception.class)
			public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
				//(注:用户名的name属性必须为username,密码的name属性必须为password。这是security判断用户输入是否正确的标准)
                // 
				logger.info("登录手机号或邮箱:======"+username);
				// 查用户 
				User user = userMapper.selectByPhoneOrEmail(username, username);
				if (user == null) {
					throw new UsernameNotFoundException(AuthContants.USER_NOT_EXIST);
				}
				SecurityUserDTO dto = new SecurityUserDTO();
				dto.setId(user.getId());
				dto.setUsername(username);
				dto.setPassword(user.getPassword());
				dto.setDisable(user.getDisable());
				// 创建securityUserDTO
//				SecurityUserDTO securityUserDTO = new SecurityUserDTO(user);
				return dto;
			}
		};
	}

}
复制代码

这是第二个类,这里会进行重定向获取code

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.bifu.distributed.auth.constant.AuthContants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    private final static Logger logger = LoggerFactory.getLogger(MyAuthenticationSuccessHandler.class);

    @Value(value = "${prefix.auth}")
    private String authPrefix; // /token

    @Value(value = "${oauth.redirectUrl}")
    private String redirectUrl; // /token

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {
        String deviceType = request.getHeader("deviceType");
        logger.info("访问设备-----------》》》" + deviceType);
        if (deviceType == null || "".equals(deviceType)) {
            deviceType = "browser";
        }
        // 重定向url到 /token 接口
        if ("browser".equals(deviceType)) {
            response.sendRedirect("http://localhost:8080:oauth/authorize?client_id=client1&response_type=code&redirect_uri=/token");
        } else if ("app".equals(deviceType)) {
            response.sendRedirect(http://localhost:8080:oauth/authorize?client_id=client2&response_type=code&redirect_uri=/token);
        }
    }

}
复制代码

因为重定向的url是:redirect_uri=/token

5.在这一步之前需要进行服务端生成公钥和密钥,每个客户端使用获取到的公钥到服务器做认证。

生成一个jks

  keytool -genkeypair -alias kevin_key -keyalg RSA -keypass 123456 -keystore kevin_key.jks -storepass 123456
复制代码

导出公钥

  keytool -list -rfc --keystore kevin_key.jks | openssl x509 -inform pem -pubkey
复制代码

保存文本public_key.txt

  -----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxeI6+R6DsGs5RW21Xu1Fur7iPwGjyngN3SCnwPtdR9jTrQ8EIak+gyjpI/g7gIacHIZKMlVFWoEgjQ7+hIQ5FHBrmSR/S81ezCFjYSjBbdrHYQjMRpn4mEWFmQhIyTRhg1Pb5oTUlWx+L3wc45r6JFdMOlgkKBvfo/7lzwGhxeNp10rfoJcnGDhlfZ3PmoIOYmvg7Z8UwszZpYHWf98164m3hMiPyc81iiy/DEE60OVVepyvynfBwg1aGDyA64w63FZ/2dSwfQ/7VQ7WWJb7oVoIy5pyHslWMuQJPpNCxpOgmb19AgC1GojDSL7WAEq+2gQFrb+7k4PyBdsRYzR9DQIDAQAB
-----END PUBLIC KEY-----
复制代码

认证服务端在Resource保存jks,认证资源服务端保存public_key.txt

image.png
image.png

下面类主要执行的方法是:accessTokenConverter

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import com.alibaba.fastjson.JSONObject;
import org.bifu.distributed.auth.constant.AuthContants;
import org.bifu.distributed.auth.dto.SecurityUserDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
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.OAuth2Authentication;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

/**
 * 认证授权服务端
 *
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    private final static Logger logger = LoggerFactory.getLogger(AuthorizationServerConfiguration.class);

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private DataSource dataSource;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //开启密码授权类型
        endpoints.authenticationManager(this.authenticationManager);
        endpoints.accessTokenConverter(accessTokenConverter());
        //配置token存储方式
        endpoints.tokenStore(tokenStore());
        endpoints.reuseRefreshTokens(false);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')");
        oauthServer.checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // jdbc方式
        clients.withClientDetails(clientDetails());
    }

    /**
     * token converter
     * 
     * @return
     */
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter() {
            /***
             * 重写增强token方法,用于自定义一些token返回的信息
             */
            @Override
            public OAuth2AccessToken enhance(OAuth2AccessToken accessToken,
                    OAuth2Authentication authentication) {
            	SecurityUserDTO securityUserDTO =
                      (SecurityUserDTO) authentication.getUserAuthentication().getPrincipal();
                logger.info("重写增强token方法= {}", JSONObject.toJSONString(securityUserDTO));
            	final Map<String, Object> additionalInformation = new HashMap<>(16);
            	additionalInformation.put("userId", securityUserDTO.getId());
            	((DefaultOAuth2AccessToken) accessToken)
            			.setAdditionalInformation(additionalInformation);
                OAuth2AccessToken enhancedToken = super.enhance(accessToken, authentication);
                return enhancedToken;
            }
        };
        // 非对称加密
        KeyStoreKeyFactory keyStoreKeyFactory =
                new KeyStoreKeyFactory(new ClassPathResource("kevin_key.jks"),
                		"123456".toCharArray());
        accessTokenConverter.setKeyPair(keyStoreKeyFactory.getKeyPair("kevin_key"));
        return accessTokenConverter;
    }

    /**
     * 定义clientDetails存储的方式-》Jdbc的方式,注入DataSource
     *
     * @return
     */
    @Bean
    public ClientDetailsService clientDetails() {
        return new JdbcClientDetailsService(dataSource);
    }

    /**
     * token store
     * 
     * @param
     * @return
     */
    @Bean
    public TokenStore tokenStore() {
        TokenStore tokenStore = new JwtTokenStore(accessTokenConverter());
        return tokenStore;
    }

}
复制代码

现在重定向到/token这个接口

/**
     * 授权,登录
     */
    @ResponseBody
    @RequestMapping(value = "/token")
    public ResultDTO<TokenResultDTO> token(HttpServletRequest request, HttpServletResponse response,
            RedirectAttributes attributes) {
        TokenResultDTO result = this.userService.token(attributes, request, response);
        logger.info("获取到token= {}", JSONObject.toJSONString(result));
        return new ResultDTO<TokenResultDTO>("200", "succ", result);
    }

复制代码
//DTO类
@Data
public class TokenResultDTO {

	private String access_token;

	private String token_type;

	private String expires_in;

	private String scope;

	private String jti;

	private String refresh_token;

	private String userId;
}
复制代码

#####访问的url路径 是固定的

 public TokenResultDTO token(RedirectAttributes attributes, HttpServletRequest request,
            HttpServletResponse response) {
        try {

            String code = request.getParameter("code");
            if (StringUtils.isEmpty(code)) {
                throw new BusinessException(AuthContants.CODE_EXCEPTION);
            }
            // 发送请求token
            String deviceType = "browser";
            if (request.getHeader("deviceType") != null && !"".equals(request.getHeader("deviceType"))) {
                deviceType = request.getHeader("deviceType");
            }
            HttpHeaders headers = new HttpHeaders();
            HttpEntity<String> entity = new HttpEntity<String>(headers);
            TokenResultDTO tokenResultDTO = null;
            if ("browser".equals(deviceType)) { 
                tokenResultDTO = this.browserRestTemplate.postForObject(
                       " http://localhost/oauth/token?client_id=client1&grant_type=authorization_code&redirect_uri=/token&code=" + code,
                        entity, TokenResultDTO.class);
            } else if ("app".equals(deviceType)) {
                tokenResultDTO = this.appRestTemplate.postForObject(
                         " http://localhost/oauth/token?client_id=client2&grant_type=authorization_code&redirect_uri=/token&code=" + code,
                        entity, TokenResultDTO.class);
            }

            return new TokenResultDTO(tokenResultDTO.getAccess_token(), tokenResultDTO.getRefresh_token(),
                    tokenResultDTO.getUserId(), tokenResultDTO.getExpires_in());
        } catch (BusinessException e) {
           
            logger.error("token?");
            throw new Exception("500", e.getMessage());
        } catch (Exception e) {
         
            logger.error("token?");
            throw new Exception("500", e.getMessage());
        }
    }
复制代码

到这里就获取到了access_token
log日志:

image.png

总结

通过日志可以出

  • 用户输入账号、密码,请求/auth/login到认证服务器
  • 认证服务器根据username检验用户,存放全局UserDetails
  • 认证服务器拦截请求判断是否合法,如果合法进入successHandler进行url重定向
  • 重定向获取的code值继续访问/token,这时JWT会对token增加,添加我们需要返回给前段的字段
  • 获得access_token和需要的字段

二 认证资源服务端

image.png

这里只讲解ResourceServerConfiguration这个类,其他都是检查异常的

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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.error.OAuth2AuthenticationEntryPoint;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.web.AuthenticationEntryPoint;

/**
 * 认证授权资源端
 * 
 * @author rs
 *
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

	@Autowired
	private MyAccessDeniedHandler myAccessDeniedHandler;

	@Autowired
	private Auth2ResponseExceptionTranslator auth2ResponseExceptionTranslator;

	@Autowired
	private SecurityAuthenticationEntryPoint securityAuthenticationEntryPoint;

	@Override
	public void configure(ResourceServerSecurityConfigurer resources) {
		resources.resourceId("client1");
		resources.tokenServices(defaultTokenServices());
		// 定义异常转换类生效
		AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
		((OAuth2AuthenticationEntryPoint) authenticationEntryPoint)
				.setExceptionTranslator(this.auth2ResponseExceptionTranslator);
		resources.authenticationEntryPoint(authenticationEntryPoint);
	}

	@Override
	public void configure(HttpSecurity http) throws Exception {
		// 放行路径在这写
		http.csrf().disable().exceptionHandling().authenticationEntryPoint(this.securityAuthenticationEntryPoint)
				.accessDeniedHandler(myAccessDeniedHandler).and().authorizeRequests()
				.antMatchers("/swagger-resources/**", "/v2/**", "/swagger/**", "/swagger**", "/webjars/**", "/aide/**",
						"/backstage/**", "/coin/**", "/talla/**", "/asset/**", "/test/**","/blockchain/borrow/**", "/back/**")
				.permitAll().anyRequest().authenticated().and().httpBasic().disable();
		// ifream的跨域设置
		http.headers().frameOptions().sameOrigin();
	}

	// ===================================================以下代码与认证服务器一致=========================================
	/**
	 * token存储,这里使用jwt方式存储
	 * 
	 * @param
	 * @return
	 */
	@Bean
	public TokenStore tokenStore() {
		TokenStore tokenStore = new JwtTokenStore(accessTokenConverter());
		return tokenStore;
	}

	/**
	 * 创建一个默认的资源服务token
	 * 
	 * @return
	 */
	@Bean
	public ResourceServerTokenServices defaultTokenServices() {
		final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
		defaultTokenServices.setTokenEnhancer(accessTokenConverter());
		defaultTokenServices.setTokenStore(tokenStore());
		return defaultTokenServices;
	}

	/**
	 * Token转换器必须与认证服务一致
	 * 
	 * @return
	 */
	@Bean
	public JwtAccessTokenConverter accessTokenConverter() {
		JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter() {
		};
		Resource resource = new ClassPathResource("public_key.txt");
		String publicKey = null;
		try {
			publicKey = inputStream2String(resource.getInputStream());
		} catch (final IOException e) {
			throw new RuntimeException(e);
		}
		accessTokenConverter.setVerifierKey(publicKey);
		return accessTokenConverter;
	}
	// ===================================================以上代码与认证服务器一致=========================================

	private String inputStream2String(InputStream is) throws IOException {
		BufferedReader in = new BufferedReader(new InputStreamReader(is));
		StringBuffer buffer = new StringBuffer();
		String line = "";
		while ((line = in.readLine()) != null) {
			buffer.append(line);
		}
		return buffer.toString();
	}

}
复制代码

主要讲解public void configure(HttpSecurity http)这个方法:我们在测试的时候往往希望直接就能访问到,不需要token,可以在这里添加放行路径。

(^o^)/~今天的幸运就到此为止吧!

热门推荐:

文末福利,最近整理一份面试资料《Java面试通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:GitHub github.com/Tingyu-Note…,关注我的掘金,更多内容陆续奉上。