我的要求是:
在我的应用程序中,当用户通过登录屏幕(而不是使用Spring Security)登录时,首次通过自定义的第三方API执行身份验证。现在,我们几乎没有使用其他服务调用的增强功能。根据要求,在从我们的应用程序进行任何休息调用之前,我们需要针对数据库重新验证用户。由于用户在使用登录屏幕登录时已经过验证,并且请求中提供了这些详细信息,我计划使用spring security来重新验证用户(预身份验证方案)。我们没有任何角色为我们的应用程序中的用户定义。因此无需担心角色。我已阅读参考手册,但我没有获得有关如何进一步处理的更多信息。我理解的一件事是,在通过定制的第三方API进行身份验证后,我们需要以某种方式告诉用户弹簧上下文。即使我谷歌搜索了一下,但无法得到一个符合我要求的好例子。 如果有人可以指导我如何从一个例子开始,那将是很好的。
我只需要告诉spring上下文类似“嘿......!此用户已经过身份验证,因此在用户的凭据经过定制的第三方验证后,可以允许他调用其余服务API。 的
我不应该更改现有的初始身份验证过程。我应该只使用经过身份验证的用户信息,并进一步使用spring security来重新验证用户。
我的问题与春季参考手册http://docs.spring.io/spring-security/site/docs/3.0.x/reference/preauth.html
中提到的问题类似请不要用单行回答(除非它有正确的答案) 外部链接)。如果你能给我看一个例子或伪代码,那就太好了。
提前致谢。
答案 0 :(得分:2)
我建议从Spring Security到第三方登录页面建立一个“桥梁”。如果您使用Spring,我认为这是最好的工作方式。
意思是,您有一个登录处理程序,可以将用户重定向到第三方登录页面。登录后,用户将被重定向回网络应用程序。
这是什么意思?听起来不错吗?它有意义吗?如果是这样,您可以使用my article获得帮助:
<security:http entry-point-ref="legacyEntryPoint">
通常,这意味着只要相关的http调用尝试访问您的应用,这就是处理请求的入口点。在您的情况下,legacyEntryPoint是您将实现的类,它将检查用户是否经过身份验证;如果没有,它会将用户重定向到第三方登录系统,否则它会使用已知的“令牌”来使用您的应用。
希望有所帮助!
答案 1 :(得分:0)
你试过这个吗?
SecurityContextHolder.getContext().setAuthenticated(true);
。
答案 2 :(得分:0)
对于第一次身份验证,我希望您有权使用某些输入值(如用户名/密码)调用第三方API,并返回true / false。如果是这样,您可以编写自己的AuthenticationProvider,如下所示。并调用您的第三方认证,如下所示。 Spring安全框架会自动设置SecurityContextHolder.getContext()。setAuthenticated(true或false);因此。你不必设置它。
public class MyAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String user = (String) authentication.getPrincipal();
String password = (String) authentication.getCredentials();
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_ONE"));
authorities.add(new SimpleGrantedAuthority("ROLE_TWO"));
UsernamePasswordAuthenticationToken authenticationToken = null;
if (<your 3rd party authentication result == true>)) {
authenticationToken = new UsernamePasswordAuthenticationToken(user, password, authorities);
} else {
throw new BadCredentialsException("Invalid credentials supplied. Please try again.");
}
return authenticationToken;
}
@Override
public boolean supports(Class<?> authentication) {
boolean supports = authentication.isAssignableFrom(UsernamePasswordAuthenticationToken.class);
return supports;
}
}
在进行进一步的REST API调用之前,在其Spring控制器中,您可以使用代码检查用户是否有效 。SecurityContextHolder.getContext()getAuthentication()isAuthenticated();
您还可以使用以下代码获取大量其他用户信息。
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
System.out.println("getAuthorities : " + authentication.getAuthorities());
System.out.println("getName : " + authentication.getName());
System.out.println("getCredentials : " + authentication.getCredentials());
System.out.println("getDetails : " + authentication.getDetails());
System.out.println("getPrincipal : " + authentication.getPrincipal());
if (authentication.getPrincipal() instanceof User) {
User user = (User) authentication.getPrincipal();
System.out.println(user.getUsername());
System.out.println(user.getPassword());
System.out.println(user.getAuthorities());
}
答案 3 :(得分:0)
我做的事非常相似。我正在为无状态REST后端进行身份验证,因此我希望用户进行一次身份验证,然后对于每个后续请求,身份验证必须是透明的。我为此使用了令牌。登录时,用户提供的凭据用于验证和生成令牌(尽管最终,我们希望使用外部服务来获取令牌)。令牌作为标头返回。然后angularjs前端在每个后续REST调用上发送令牌。后端检查令牌的有效性,如果它是好的,则标记已经过验证的令牌。是真的。
这是我的security-context.xml:
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<http use-expressions="true"
entry-point-ref="restAuthenticationEntryPoint"
create-session="stateless">
<intercept-url pattern="/secured/extreme/**" access="hasRole('ROLE_SUPERVISOR')"/>
<intercept-url pattern="/secured/**" access="isAuthenticated()" />
<intercept-url pattern="/j_spring_security_check" requires-channel="https" access="permitAll"/>
<intercept-url pattern="/logon.jsp" requires-channel="https" access="permitAll"/>
<sec:custom-filter ref="authenticationTokenProcessingFilter" position="FORM_LOGIN_FILTER" />
</http>
<beans:bean id="restAuthenticationEntryPoint" class="com.company.project.authentication.security.RestAuthenticationEntryPoint" />
<beans:bean id="authenticationTokenProcessingFilter" class="com.company.project.authentication.security.AuthenticationTokenProcessingFilter" >
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="userDetailsServices">
<beans:list>
<beans:ref bean="inMemoryUserDetailsService" />
<beans:ref bean="tmpUserDetailsService" />
</beans:list>
</beans:property>
</beans:bean>
<beans:bean id="tmpUserDetailsService" class="com.company.project.authentication.security.TokenUserDetailsServiceImpl" />
<user-service id="inMemoryUserDetailsService">
<user name="temporary" password="temporary" authorities="ROLE_SUPERVISOR" />
<user name="user" password="userPass" authorities="ROLE_USER" />
</user-service>
<authentication-manager alias="authenticationManager">
<!-- Use some hard-coded values for development -->
<authentication-provider user-service-ref="inMemoryUserDetailsService" />
<authentication-provider ref='companyLdapProvider' />
</authentication-manager>
对于身份验证过滤器,我将UsernamePasswordAuthenticationFilter子类化。当它是登录请求时,则会发生身份验证提供程序的身份验证,然后生成令牌。如果从标头中读取令牌,则检查令牌以进行身份验证。这是我的身份验证过滤器(它仍然没有生产就绪,但它可以帮助您了解您可以做什么):
public class AuthenticationTokenProcessingFilter extends UsernamePasswordAuthenticationFilter {
//~ Static fields/initializers =====================================================================================
private static final String HEADER_AUTH_TOKEN = "X-Auth-Token";
private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationTokenProcessingFilter.class);
private List<UserDetailsService> userDetailsServices = new ArrayList<UserDetailsService>();
//~ Constructors ===================================================================================================
public AuthenticationTokenProcessingFilter() {
super();
}
//~ Methods ========================================================================================================
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String authToken = this.extractAuthTokenFromRequest(request);
if (authToken == null) {
super.doFilter(request, res, chain);
return;
}
String userName = TokenUtils.getUserNameFromToken(authToken);
if (userName != null) {
UserDetails userDetails = loadUserByUsername(userName);
if (TokenUtils.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
UsernamePasswordAuthenticationToken authRequest = authenticateWithForm(request, response);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
Authentication authentication = this.getAuthenticationManager().authenticate(authRequest);
if (authentication.isAuthenticated()) {
try {
String authToken = TokenUtils.createToken(obtainUsername(request), obtainPassword(request));
LOGGER.info("Setting HTTP header {} = {}", HEADER_AUTH_TOKEN, authToken);
response.addHeader(HEADER_AUTH_TOKEN, authToken);
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
LOGGER.info("authorities = {}", authorities);
// Now we should make an in-memory table of the token and userdetails for later use
} catch(Exception e) {
LOGGER.warn("Error creating token for authentication. Authorization token head cannot be created.", e);
}
}
return authentication;
}
protected UsernamePasswordAuthenticationToken authenticateWithForm(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
return authRequest;
}
private String extractAuthTokenFromRequest(HttpServletRequest httpRequest) {
/* Get token from header */
String authToken = httpRequest.getHeader(HEADER_AUTH_TOKEN);
/* If token not found get it from request parameter */
if (authToken == null) {
authToken = httpRequest.getParameter("token");
}
return authToken;
}
public List<UserDetailsService> getUserDetailsServices() {
return userDetailsServices;
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsServices.add(userDetailsService);
}
public void setUserDetailsServices(List<UserDetailsService> users) {
if (users != null) {
this.userDetailsServices.clear();
this.userDetailsServices.addAll(users);
}
}
private UserDetails loadUserByUsername(String username) {
UserDetails user = null;
List<Exception> exceptions = new ArrayList<Exception>();
for (UserDetailsService service: userDetailsServices) {
try {
user = service.loadUserByUsername(username);
break;
} catch (Exception e) {
LOGGER.warn("Could not load user by username {} with service {}", username, service.getClass().getName());
LOGGER.info("Exception is: ",e);
exceptions.add(e);
}
}
if (user == null && !exceptions.isEmpty()) {
throw new AuthenticationException(exceptions.get(0));
}
return user;
}
}
我仍然致力于改进UserDetailsService。通常,您可以使用身份验证提供程序来获取UserDetails,但由于我有一个无状态应用程序,因此当我想验证令牌时,我必须确定要使用哪个UserDetailsService。我暂时使用自定义代码执行此操作。