【发布时间】:2021-07-31 04:30:18
【问题描述】:
我正在尝试从另一个(angularjs)调用一个应用程序(spring-boot 应用程序)上的 REST 端点。应用程序正在以下主机和端口上运行。
- REST 应用程序,使用 Spring Boot,
http://localhost:8080 - HTML 应用程序,使用 angularjs,
http://localhost:50029
我还在 spring-boot 应用程序中使用 spring-security。从 HTML 应用程序中,我可以对 REST 应用程序进行身份验证,但此后,我仍然无法访问任何 REST 端点。例如,我有一个 angularjs 服务定义如下。
adminServices.factory('AdminService', ['$resource', '$http', 'conf', function($resource, $http, conf) {
var s = {};
s.isAdminLoggedIn = function(data) {
return $http({
method: 'GET',
url: 'http://localhost:8080/api/admin/isloggedin',
withCredentials: true,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
});
};
s.login = function(username, password) {
var u = 'username=' + encodeURI(username);
var p = 'password=' + encodeURI(password);
var r = 'remember_me=1';
var data = u + '&' + p + '&' + r;
return $http({
method: 'POST',
url: 'http://localhost:8080/login',
data: data,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
});
};
return s;
}]);
angularjs 控制器如下所示。
adminControllers.controller('LoginController', ['$scope', '$http', 'AdminService', function($scope, $http, AdminService) {
$scope.username = '';
$scope.password = '';
$scope.signIn = function() {
AdminService.login($scope.username, $scope.password)
.success(function(d,s) {
if(d['success']) {
console.log('ok authenticated, call another REST endpoint');
AdminService.isAdminLoggedIn()
.success(function(d,s) {
console.log('i can access a protected REST endpoint after logging in');
})
.error(function(d, s) {
console.log('huh, error checking to see if admin is logged in');
$scope.reset();
});
} else {
console.log('bad credentials?');
}
})
.error(function(d, s) {
console.log('huh, error happened!');
});
};
}]);
在拨打http://localhost:8080/api/admin/isloggedin 时,我收到了401 Unauthorized。
在 REST 应用程序端,我有一个如下所示的 CORS 过滤器。
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {
@Override
public void destroy() { }
@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", "http://localhost:50029");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, X-Auth-Token");
response.setHeader("Access-Control-Allow-Credentials", "true");
if(!"OPTIONS".equalsIgnoreCase(request.getMethod())) {
chain.doFilter(req, res);
}
}
@Override
public void init(FilterConfig config) throws ServletException { }
}
我的 spring 安全配置如下所示。
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
@Autowired
private JsonAuthSuccessHandler jsonAuthSuccessHandler;
@Autowired
private JsonAuthFailureHandler jsonAuthFailureHandler;
@Autowired
private JsonLogoutSuccessHandler jsonLogoutSuccessHandler;
@Autowired
private AuthenticationProvider authenticationProvider;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private PersistentTokenRepository persistentTokenRepository;
@Value("${rememberme.key}")
private String rememberMeKey;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/", "/admin", "/css/**", "/js/**", "/fonts/**", "/api/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.successHandler(jsonAuthSuccessHandler)
.failureHandler(jsonAuthFailureHandler)
.permitAll()
.and()
.logout()
.deleteCookies("remember-me", "JSESSIONID")
.logoutSuccessHandler(jsonLogoutSuccessHandler)
.permitAll()
.and()
.rememberMe()
.userDetailsService(userDetailsService)
.tokenRepository(persistentTokenRepository)
.rememberMeCookieName("REMEMBER_ME")
.rememberMeParameter("remember_me")
.tokenValiditySeconds(1209600)
.useSecureCookie(false)
.key(rememberMeKey);
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(authenticationProvider);
}
}
所有处理程序所做的只是根据用户是否登录、身份验证失败或注销来写出类似{success: true} 的 JSON 响应。 RestAuthenticationEntryPoint 如下所示。
@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest req, HttpServletResponse resp, AuthenticationException ex)
throws IOException, ServletException {
resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
对我遗漏或做错了什么有任何想法?
【问题讨论】:
-
我想您还需要进行身份验证,例如令牌或其他东西。您有 2 台服务器。你看过那个教程吗?spring.io/guides/tutorials/spring-security-and-angular-js
-
@GokhanOner 我如何进行身份验证?这可能是这个问题的缺失部分。另外,是的,我确实阅读了这些教程,并且认为它们与我的方法不一致。前两部分处理 Http-Basic 身份验证,然后第三部分处理 Redis(我不想或不打算将其作为依赖项),然后最后一个教程是关于
API Gatewaywith spring cloud,我认为有点矫枉过正。 -
我想你可以不使用redis,它只是一个键值缓存存储。您需要将身份验证和 CSRF 令牌存储在存储中,可能在动态内部映射。这里的关键是身份验证密钥。查看示例:github.com/dsyer/spring-security-angular/tree/master/… 和带有“资源服务器”的页面。您将看到定义了一些额外的 bean,CORS 过滤器的顺序也很重要。还有一些道具。也需要改变。
-
好的,我做了一个快速的研究。要摆脱 Redis,您只需要创建一个 springSessionRepositoryFilter bean,查看github.com/spring-projects/spring-session/blob/1.0.0.RC1/…,还有 sessionRepository bean,在这个 bean 中,您可以使用 MapSessionRepository,而不是 RedisOperationsSessionRepository,它也在 spring-session 中。然后按照例子来。
标签: angularjs rest spring-mvc spring-boot cors