我们正在构建一些将通过 RESTful API 公开的服务。该 API 的主要客户是使用 Angular JS 的 Liferay portlet,这意味着可以从客户端(Angular)直接调用我们的服务。
到目前为止,我们已经设计了一种身份验证和授权机制,以确保我们能够识别哪个登录用户 (Liferay) 正在请求我们的 API。
PS.:请注意,虽然我们使用的是 Liferay,但它也可以是任何其他基于 Java 的应用程序。
我们的设计是:
- 当用户登录我们的门户时,Liferay 会使用 userLogin(或 ID)+ 客户端 IP + 时间戳创建一个身份验证 token 。该 token 保存在 cookie 中;
- 在每次 REST 调用之前,Angular 都会读取此 Cookie 并通过 HTTP header 发送其内容;
- 我们的服务会“解密”发送的 Cookie 内容,并验证时间戳是否有效、IP 是否相同,以及根据我们的业务规则,用户是否有权执行或阅读他想做的任何事情。
这种设计目前对我们来说看起来是一致的,并且根据我们选择创建此 token 的算法,我们相信这是一种安全的方法。
我们的疑问是:
- 我们是否在某种程度上重新发明轮子而不使用某种自定义提供程序的 HTTP 身份验证?怎么做?
- Spring Security 可以帮助我们解决这个问题吗?我们已经阅读了一些有关它的文章,但不清楚将它与非 Spring 应用程序一起使用是否有意义;
- 这种方法是否存在我们未考虑到的安全缺陷?
提前谢谢您。如有任何帮助,我们将不胜感激。
菲利普
最佳答案
Spring security 解决了问题描述,作为奖励,您将免费获得所有 Spring security 功能。
token 方法很棒,以下是如何使用 spring-security 保护您的 API 实现 AuthenticationEntryPoint 并将开始方法设置为 401 而不是重定向 3XX,如下所示
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Access Denied");
- 让 TokenProcessingFilter 扩展并利用 UsernamePasswordAuthenticationFilter 提供的功能,重写 doFilter() 方法,从请求 header 中提取 token ,按如下方式验证和验证 token
@覆盖
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = this.getAsHttpRequest(request);
String authToken = this.extractAuthTokenFromRequest(httpRequest);
String userName = TokenUtils.getUserNameFromToken(authToken);
if (userName != null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(userName);
if (TokenUtils.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication =new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
}
您的 Spring-security 配置将如下所示
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthFailure authFailure;
@Autowired
private AuthSuccess authSuccess;
@Autowired
private EntryPointUnauthorizedHandler unauthorizedHandler;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private AuthenticationTokenProcessingFilter authTokenProcessingFilter;
@Autowired
public void configureAuthBuilder(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // Restful hence stateless
.and()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler) // Notice the entry point
.and()
.addFilter(authTokenProcessingFilter) // Notice the filter
.authorizeRequests()
.antMatchers("/resources/**", "/api/authenticate").permitAll()
//.antMatchers("/admin/**").hasRole("ADMIN")
//.antMatchers("/providers/**").hasRole("ADMIN")
.antMatchers("/persons").authenticated();
}
}
--最后您将需要另一个端点来进行身份验证和 token 生成 这是一个 Spring MVC 示例
@Controller
@RequestMapping(value="/api")
public class TokenGenerator{
@Autowired
@Lazy
private AuthenticationManager authenticationManager;
@Autowired
private UtilityBean utilityBean;
@Autowired
private UserDetailsService userDetailsService;
@RequestMapping(value="/authenticate", method=RequestMethod.POST, consumes=MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<?> generateToken(@RequestBody EmefanaUser user){
ResponseEntity<?> response = null;
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserId(),user.getCredential());
try {
Authentication authentication = authenticationManager.authenticate(authenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
/*
* Reload user as password of authentication principal will be null
* after authorization and password is needed for token generation
*/
UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUserId());
String token = TokenUtils.createToken(userDetails);
response = ResponseEntity.ok(new TokenResource(utilityBean.encodePropertyValue(token)));
} catch (AuthenticationException e) {
response = ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
return response;
}
}
1 生成 token ,2.后续 API 调用应具有 token 是的,spring-security 可以做到这一点,并且您不必在身份验证、授权方面开辟新的领域。
- 希望这有帮助
关于spring - 使用 Liferay 进行 REST 服务中的身份验证和授权,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28242604/