Spring Security - 基于令牌的 API 身份验证和用户/密码身份验证
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21994348/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring Security - Token based API auth & user/password authentication
提问by rhinds
I am trying to create a webapp that will primarily provide a REST API using Spring, and am trying to configure the security side.
我正在尝试创建一个主要使用 Spring 提供 REST API 的 web 应用程序,并且正在尝试配置安全方面。
I am trying to implement this kind of pattern: https://developers.google.com/accounts/docs/MobileApps(Google have totally changed that page, so no longer makes sense - see the page I was referring to here: http://web.archive.org/web/20130822184827/https://developers.google.com/accounts/docs/MobileApps)
我正在尝试实现这种模式:https: //developers.google.com/accounts/docs/MobileApps(谷歌已经完全改变了那个页面,所以不再有意义 - 请参阅我在这里指的页面:http: //web.archive.org/web/20130822184827/https://developers.google.com/accounts/docs/MobileApps)
Here is what I need to accompish:
这是我需要完成的:
- Web app has simple sign-in/sign-up forms that work with normal spring user/password authentication (have done this type of thing before with dao/authenticationmanager/userdetailsservice etc)
- REST api endpoints that are stateless sessions and every request authenticated based ona token provided with the request
- Web 应用程序具有简单的登录/注册表单,可与普通的 spring 用户/密码身份验证一起使用(之前已使用 dao/authenticationmanager/userdetailsservice 等做过此类事情)
- REST api 端点是无状态会话,每个请求都基于请求提供的令牌进行身份验证
(e.g. user logins/signs up using normal forms, webapp provides secure cookie with token that can then be used in following API requests)
(例如,用户使用普通形式登录/注册,webapp 提供带有令牌的安全 cookie,然后可以在以下 API 请求中使用该令牌)
I had a normal authentication setup as below:
我有一个正常的身份验证设置,如下所示:
@Override protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.antMatchers("/mobile/app/sign-up").permitAll()
.antMatchers("/v1/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/")
.loginProcessingUrl("/loginprocess")
.failureUrl("/?loginFailure=true")
.permitAll();
}
I was thinking of adding a pre-auth filter, that checks for the token in the request and then sets the security context (would that mean that the normal following authentication would be skipped?), however, beyond the normal user/password I have not done too much with token based security, but based on some other examples I came up with the following:
我正在考虑添加一个 pre-auth 过滤器,检查请求中的令牌,然后设置安全上下文(这是否意味着将跳过正常的后续身份验证?),但是,超出了我拥有的普通用户/密码基于令牌的安全性没有做太多,但基于其他一些例子,我想出了以下几点:
Security Config:
安全配置:
@Override protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.addFilter(restAuthenticationFilter())
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint()).and()
.antMatcher("/v1/**")
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.antMatchers("/mobile/app/sign-up").permitAll()
.antMatchers("/v1/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/")
.loginProcessingUrl("/loginprocess")
.failureUrl("/?loginFailure=true")
.permitAll();
}
My custom rest filter:
我的自定义休息过滤器:
public class RestAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public RestAuthenticationFilter(String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
}
private final String HEADER_SECURITY_TOKEN = "X-Token";
private String token = "";
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
this.token = request.getHeader(HEADER_SECURITY_TOKEN);
//If we have already applied this filter - not sure how that would happen? - then just continue chain
if (request.getAttribute(FILTER_APPLIED) != null) {
chain.doFilter(request, response);
return;
}
//Now mark request as completing this filter
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
//Attempt to authenticate
Authentication authResult;
authResult = attemptAuthentication(request, response);
if (authResult == null) {
unsuccessfulAuthentication(request, response, new LockedException("Forbidden"));
} else {
successfulAuthentication(request, response, chain, authResult);
}
}
/**
* Attempt to authenticate request - basically just pass over to another method to authenticate request headers
*/
@Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
AbstractAuthenticationToken userAuthenticationToken = authUserByToken();
if(userAuthenticationToken == null) throw new AuthenticationServiceException(MessageFormat.format("Error | {0}", "Bad Token"));
return userAuthenticationToken;
}
/**
* authenticate the user based on token, mobile app secret & user agent
* @return
*/
private AbstractAuthenticationToken authUserByToken() {
AbstractAuthenticationToken authToken = null;
try {
// TODO - just return null - always fail auth just to test spring setup ok
return null;
} catch (Exception e) {
logger.error("Authenticate user by token error: ", e);
}
return authToken;
}
The above actually results in an error on app startup saying: authenticationManager must be specified
Can anyone tell me how best to do this - is a pre_auth filter the best way to do this?
以上实际上导致应用程序启动时出错:authenticationManager must be specified
有人能告诉我如何最好地做到这一点 - pre_auth 过滤器是做到这一点的最佳方法吗?
EDIT
编辑
I wrote up what I found and how I did it with Spring-security (including the code) implementing a standard token implementation (not OAuth)
我写了我的发现以及我如何使用 Spring-security(包括代码)实现标准令牌实现(不是 OAuth)
Overview of the problem and approach/solution
Implementing the solution with Spring-security
Hope it helps some others..
希望它可以帮助其他人..
采纳答案by Dave Syer
I believe the error that you mention is just because the AbstractAuthenticationProcessingFilter
base class that you are using requires an AuthenticationManager
. If you aren't going to use it you can set it to a no-op, or just implement Filter
directly. If your Filter
can authenticate the request and sets up the SecurityContext
then usually the downstream processing will be skipped (it depends on the implementation of the downstream filters, but I don't see anything weird in your app, so they probably all behave that way).
我相信您提到的错误只是因为AbstractAuthenticationProcessingFilter
您使用的基类需要一个AuthenticationManager
. 如果您不打算使用它,您可以将其设置为无操作,或者直接实现Filter
。如果您Filter
可以对请求进行身份验证并进行设置,SecurityContext
那么通常会跳过下游处理(这取决于下游过滤器的实现,但我在您的应用程序中没有看到任何奇怪的东西,因此它们可能都以这种方式运行)。
If I were you I might consider putting the API endpoints in a completely separate filter chain (another WebSecurityConfigurerAdapter
bean). But that only makes things easier to read, not necessarily crucial.
如果我是你,我可能会考虑将 API 端点放在一个完全独立的过滤器链(另一个WebSecurityConfigurerAdapter
bean)中。但这只会让事情更容易阅读,并不一定是至关重要的。
You might find (as suggested in comments) that you end up reinventing the wheel, but no harm in trying, and you will probably learn more about Spring and Security in the process.
您可能会发现(如评论中所建议的)您最终会重新发明轮子,但尝试并没有什么坏处,并且您可能会在此过程中了解更多有关 Spring 和 Security 的信息。
ADDITION: the github approach is quite interesting: users just use the token as a password in basic auth, and the server doesn't need a custom filter (BasicAuthenticationFilter
is fine).
附加:github 方法非常有趣:用户只需在基本身份验证中使用令牌作为密码,服务器不需要自定义过滤器(BasicAuthenticationFilter
很好)。