Я пытаюсь реализовать безопасность Oauth2.0 с помощью Springboot (версия 1.5.10.RELEASE). У меня есть родительский проект со следующими модулями
- Родительское приложение
- OAuth-Серверный модуль (работает на порту: 9999)
- Ресурсный серверный модуль (работает на порту: 9000)
- Веб-модуль (работает на порту: 8080)
- Общий модуль
Каждый модуль является приложением SpringBoot.
У меня есть следующие файлы конфигурации в Oauth2-Server-Module
@Configuration
public class AuthServerConfigAdapter extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AuthServerSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
DataSource dataSource;
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.formLogin().loginPage("/login").permitAll().and().requestMatchers()
.antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access").and().authorizeRequests()
.anyRequest().authenticated();
// @formatter:on
}
}
@Configuration
@EnableAuthorizationServer
@Order(2)
public class Oauth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
private static final Logger _log = LoggerFactory.getLogger(Oauth2AuthorizationConfig.class);
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
DataSource dataSource;
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
// TODO: Change for production
KeyPair keyPair = new KeyStoreKeyFactory(new ClassPathResource("keystore.jks"), "Prism@xic-dw".toCharArray())
.getKeyPair("doctor-world");
converter.setKeyPair(keyPair);
return converter;
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
_log.info(" 3 --- Authorization Server Security is Configured!!!");
security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
_log.info(" 2 --- Clients to whom access is provided are configured here!!!");
clients.jdbc(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
_log.info(" 1 --- Authorization Server Endpoints are configured here!!!");
endpoints.tokenServices(defaultTokenServices());
endpoints.authenticationManager(authenticationManager).accessTokenConverter(jwtAccessTokenConverter());
}
@Bean
public JwtTokenStore tokenStore() {
JwtTokenStore store = new JwtTokenStore(jwtAccessTokenConverter());
return store;
}
@Bean
public TokenEnhancerChain tokenEnhancerChain() {
final TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(new MyTokenEnhancer(), jwtAccessTokenConverter()));
return tokenEnhancerChain;
}
@Bean
public DefaultTokenServices defaultTokenServices() {
final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setClientDetailsService(clientDetailsService);
defaultTokenServices.setTokenEnhancer(tokenEnhancerChain());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
private static class MyTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
if (authentication.getPrincipal() instanceof User) {
final User user = (User) authentication.getPrincipal();
final Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put(DWConstants.USER_ID, user.getId());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
}
return accessToken;
}
}
}
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCorsFilter implements Filter {
public static final Logger _log = LoggerFactory.getLogger(SimpleCorsFilter.class);
public SimpleCorsFilter() {
_log.info("SimpleCORSFilter init");
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
response.setHeader("Cache-Control", "no-store");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void destroy() {
}
}
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
Set<GrantedAuthority> grantedAuthorities = new HashSet<GrantedAuthority>();
for (Role role : user.getRoles()) {
grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
}
user.setAuthorities(grantedAuthorities);
return user;
}
}
У меня также есть следующие файлы конфигурации в Resource-Server-Module
@SpringBootApplication
@EnableResourceServer
public class ResourceServerApplication {
public static void main(String[] args) {
SpringApplication.run(ResourceServerApplication.class, args);
}
}
@Configuration
public class AuthResourceSecurityConfig extends ResourceServerConfigurerAdapter {
@Value("${public.url.roles}")
private String[] publicUrlroles;
@Value("${user.url.roles}")
private String[] userUrlroles;
@Override
public void configure(HttpSecurity http) throws Exception {
http
// .csrf().disable()
.authorizeRequests()
.antMatchers(publicUrlroles).permitAll()
.antMatchers(userUrlroles).hasAnyRole("USER","ADMIN")
.antMatchers("/v2/api-docs", "/configuration/**", "/swagger-resources/**", "/swagger-ui.html", "/webjars/**", "/api-docs/**")
.permitAll().anyRequest().denyAll().and().csrf().disable();
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("myResource");
}
}
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CorsBypassFilter implements Filter {
public CorsBypassFilter() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
response.setHeader("Cache-Control", "no-store");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void destroy() {
}
}
@Component
public class RequestEnricherFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
if (SecurityContextHolder.getContext().getAuthentication()
.getDetails() instanceof OAuth2AuthenticationDetails) {
String accessToken = ((OAuth2AuthenticationDetails) SecurityContextHolder.getContext().getAuthentication()
.getDetails()).getTokenValue();
Jwt decodedTokenJwt = JwtHelper.decode(accessToken);
try {
JSONObject jwtJSONOBject = new JSONObject(decodedTokenJwt.getClaims());
if (!jwtJSONOBject.isNull("userId")) {
servletRequest.setAttribute("userId",
Long.parseLong(jwtJSONOBject.get("userId").toString()));
servletRequest.setAttribute("user_name",
jwtJSONOBject.get("user_name").toString());
}
} catch (JSONException e) {
// TODO: add logger
e.printStackTrace();
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
@Override
public void destroy() { }
}
application.properties Файл Resource-Server-Module
#Database Configuration
#Used By anyone - without Login
public.url.roles=/v1/public/greetings
#Used By registered User in our system (By Admin Or User)
user.url.roles=/v1/user/user-detail/{userId}
#Below is the public key used to verify the jwt Signature which is coming in request. Replace it with Production public key
security.oauth2.resource.jwt.keyValue: -----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhYL3dk8NGj7d+H6YTFS35DiH6SyQJoeWvepSo+Nwwz/TBkKP0q2wNmDoYfzTy1enZpZNbdLpBxyPbvckjeHn+UAQutsSTmdMlf9itYSjGCzwwYAjgdk8ouJfodI3RhOx76Wz7VwO16pSTr2cRLSD2rFWPlRGS+v2fr+Q0PeEwOxQaeva54wFxLfQp/Os6T4AaD/F88YXO3VFb9fjLbQJWf+wD9DaDoiAcvT3ngxEGzY73DoH+VuoCoMnQlI2XbUI4hR91Hs6ePqbr2DDW5Th/Wzk33ZcGdHXyIfZDbSzfAFuOhpo4i03knzEtaAHuvAi5OPmpYSsrhQyrCUpYoLLqwIDAQAB\n-----END PUBLIC KEY-----
Web-модуль:
Oauth-server-module & Resource-server-Module Dependencies are included in pom.xml of web-module :
</dependencies>
<dependency>
<groupId>com.poc</groupId>
<artifactId>authserver</artifactId>
</dependency>
<dependency>
<groupId>com.poc</groupId>
<artifactId>resource-server</artifactId>
</dependency>
</dependencies>
@SpringBootApplication
@CrossOrigin(origins = { "http://my-domain.com", "http://localhost:4200" })
public class WebApplication {
public static void main(String[] args) {
SpringApplication.run(WebApplication .class, args);
}
}
application.properties Файл Web-модуля
#Database Configuration
#Used By anyone - without Login
public.url.roles=/v1/public/greetings
#Used By registered User in our system (By Admin Or User)
user.url.roles=/v1/user/user-detail/{userId}
#Below is the public key used to verify the jwt Signature which is coming in request. Replace it with Production public key
security.oauth2.resource.jwt.keyValue: -----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhYL3dk8NGj7d+H6YTFS35DiH6SyQJoeWvepSo+Nwwz/TBkKP0q2wNmDoYfzTy1enZpZNbdLpBxyPbvckjeHn+UAQutsSTmdMlf9itYSjGCzwwYAjgdk8ouJfodI3RhOx76Wz7VwO16pSTr2cRLSD2rFWPlRGS+v2fr+Q0PeEwOxQaeva54wFxLfQp/Os6T4AaD/F88YXO3VFb9fjLbQJWf+wD9DaDoiAcvT3ngxEGzY73DoH+VuoCoMnQlI2XbUI4hR91Hs6ePqbr2DDW5Th/Wzk33ZcGdHXyIfZDbSzfAFuOhpo4i03knzEtaAHuvAi5OPmpYSsrhQyrCUpYoLLqwIDAQAB\n-----END PUBLIC KEY-----
Когда я пытаюсь протестировать мой оставшийся API с помощью почтальона, тогда он будет нормально работать на моей локальной машине.
Example: я могу получить access_token, используя почтальон через http://localhost:9999/oauth/token?grant_type=password&username=user&password=password
Также получить все данные, используя http://localhost:8080/v1/public/greetings & http://localhost:8080/v1/user/user-detail/{userId} после передачи access_token
Теперь я создаю три .jar-файла для каждого модуля и запускаю его на своем выделенном сервере и пытаюсь получить access_token, используя http://xxx.xxx.xx.xx:9999/oauth/token?grant_type=password&username=user&password=password, тогда он работает нормально
Но когда я пытаюсьдля получения данных с помощью http://xxx.xxx.xx.xx:8080/v1/public/greetings будет отображаться ошибка 401-Несанкционированный
Error : Access to XMLHttpRequest at 'xxx.xxx.xx.xx::8080/v1/public/greetings' from origin 'mydomain.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Заранее спасибо.