Какие? Вы не пробовали OAuth2, который используют github, Alipay и WeChat?

Java
Какие? Вы не пробовали OAuth2, который используют github, Alipay и WeChat?

Какие? Вы никогда не пробовали OAuth2, который используется github, Alipay и WeChat? Сегодня я в основном объясню процесс аутентификации входа, включая часть исходного кода.Я также наступил на много ям в то время.Я проанализировал весь процесс самостоятельно, записывая, и вы можете найти меня, если вам нужно исходный код!

1. Полная аутентификация и авторизация на основе безопасности OAuth 2.0+JWT+spring.

Сначала проанализируйте, почему вы используете для этого OAuth2 и JWT.

  1. Единый вход (SSO): Сценарий Единый вход — наиболее распространенное решение, но SSO требует, чтобы каждая служба, взаимодействующая с пользователем, взаимодействовала со службой проверки подлинности, что не только создает дублирование, но иТакже генерирует много тривиального сетевого трафика;
  2. Распределенный сеанс (сеанс): схема реализует распределенное сопоставление хэшей, сохраняя информацию о сеансе пользователя в общем хранилище, таком как Redis, и используя идентификатор сеанса пользователя в качестве ключа. Когда пользователь обращается к микросервису, данные сеанса доступны из общего хранилища. Это решение очень хорошо с точки зрения высокой доступности и расширения, но поскольку информация о сеансе хранится в общем хранилище, для защиты безопасности данных требуется определенный механизм защиты, поэтому он будет реализован в конкретных реализациях.имеют более высокую сложность.
  3. Токен клиента (токен): токен схемы создается клиентом и подписывается сервером аутентификации. Токен будет содержать достаточно информации, чтобы клиент мог добавить токен к запросу при запросе, таким образом предоставляя данные удостоверения пользователя для каждой микрослужбы. Эта схема решает проблему безопасности схемы распределенного сеанса,А вот как вовремя вывести данные аутентификации пользователя — большая проблема, хотя можно использовать краткосрочные токены и часто сверяться с сервером аутентификации., но не полностью решен. JWT (JSON Web Tokens) — очень известное решение для токенов на стороне клиента, достаточно простое и имеющее высокую степень поддержки различных сред.
  4. Токен клиента в сочетании со шлюзом API

При реализации шлюза API в архитектуре микрослужб необработанный токен клиента можно преобразовать во внутренний токен сеанса. С одной стороны, микросервисы могут быть эффективно скрыты, а с другой стороны, обработка выхода токена может быть реализована через унифицированный вход API-шлюза. Во втором решении Дэвида Борсоса: Распределенный сеанс, разработчики должны иметь возможность разделять информацию о сеансе пользователя для централизованного управления. Относительно зрелым проектом с открытым исходным кодом в отрасли является Spring Session, который использует базу данных Redis или механизм кэширования для реализации хранилища сеансов и реализует автоматическую загрузку данных сеанса через фильтры. С развитием приложений облачных сервисов в последние годы использование аутентификации на основе токенов становится все шире и шире. Для аутентификации на основе токенов он обычно включает следующие значения:

  • Токен — это набор аутентифицированной информации о пользователе, а не просто бессмысленный идентификатор.
  • Токен уже содержит достаточно информации, и токен проверки может завершить проверку личности пользователя, тем самым уменьшая нагрузку на получение базы данных для проверки пользователя и повышая производительность системы.
  • Поскольку токен должен быть подписан и выдан сервером, если токен проходит декодирование и аутентификацию, мы можем считать, что информация, содержащаяся в токене, является законной и действительной.
  • Сервер получит информацию о токене через авторизацию в заголовке HTTP и проверит ее, и нет необходимости хранить какую-либо информацию на стороне сервера.
  • Аутентификацию на основе токенов можно использовать в браузерных клиентах и ​​приложениях для мобильных устройств или в сторонних приложениях с помощью механизма проверки токенов сервера.
  • Могут поддерживаться межпрограммные вызовы. На основе куки доступ к домену не разрешен, а у токенов этой проблемы нет.

Подводя итог, аутентификация на основе токенов содержит соответствующую информацию об аутентифицированном пользователе, поэтому проверка личности пользователя может быть завершена путем проверки токена, что полностью отличается от предыдущей аутентификации на основе сеанса. Поэтому, основываясь на этом преимуществе токенов, такие как T WeChat, Alipay, Weibo и GitHub и т. д., запустили службы аутентификации на основе токенов для доступа к открытому API и единому входу. Далее мы сосредоточимся на OAuth 2.0 и JWT в схеме аутентификации на основе токенов.

2. Две части: сервер аутентификации (аутентификация и генерация токенов), сервер ресурсов аутентификации (доступ к ресурсам в других сервисах требует проверки)

rs.png

3. Давайте посмотрим на режим авторизации клиента (обычно используется режим кода авторизации, просто вы хотите перенаправить сервер аутентификации URL для получения кода авторизации (кода), получить токен доступа.

После первого шага в приведенной выше блок-схеме он будет перенаправлен на что-то вроде

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

вернет код во время посещения

http://localhost:8080/oauth/token?client_id=client1&grant_type=authorization_code&redirect_uri=/token&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 (получит входящее имя пользователя) -> protected void configure (HttpSecurity http) -> здесь выполнится 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;
			}
		};
	}

}

Это второй класс, сюда будет перенаправлен для получения кода

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-----

Сервер аутентификации сохраняет jks в Resource, а сервер ресурсов аутентификации сохраняет 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 здесь лог лог:

image.png

Суммировать

через журнал

  • Пользователь вводит учетную запись и пароль и запрашивает /auth/login на сервере аутентификации.
  • Сервер аутентификации проверяет пользователя в соответствии с именем пользователя и сохраняет глобальные сведения о пользователе.
  • Сервер аутентификации перехватывает запрос, чтобы определить, является ли он допустимым, и, если он является допустимым, входит в SuccessHandler для перенаправления URL-адреса.
  • Значение кода, полученное при перенаправлении, продолжает обращаться к /token, в это время JWT увеличит токен и добавит нужные нам поля, чтобы вернуться к предыдущему абзацу.
  • Получить 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 (HttpSecurity http): мы часто хотим иметь доступ к нему напрямую при тестировании, без необходимости в токене, вы можете добавить здесь путь выпуска.

(^o^)/~ Сегодняшняя удача заканчивается здесь!

Популярная рекомендация:

В конце статьи мы недавно составили материал для интервью «Руководство по прохождению интервью по Java», в котором рассматриваются основные технологии Java, JVM, параллелизм Java, SSM, микросервисы, базы данных, структуры данных и многое другое. способ получения:GitHub GitHub.com/ting с -note…, обратите внимание на мои самородки, больше контента будет предлагаться один за другим.