我们正在构建一些通过RESTful API公开的服务。这个API的主要客户是使用Angular JS的Liferay门户,这意味着从客户端(Angular)直接调用我们的服务。
到目前为止,我们已经设计了一种身份验证和授权机制,以确保我们可以“识别请求我们API的已登录用户(Liferay)”。
注意,尽管我们使用的是Liferay,但也可以是任何其他基于Java的应用程序。
我们设计的内容如下:
- 当用户在我们的门户登录时,Liferay会创建一个身份验证令牌,其中包括用户登录名(或ID)+客户端IP+时间戳。这个令牌被保存在一个cookie中;
- 在每个REST调用之前,Angular会读取这个cookie并通过HTTP头发送其内容;
- 我们的服务“解密”发送的cookie内容,并验证时间戳是否有效,IP是否相同,并根据我们的业务规则,判断用户是否有权限进行操作或阅读。
目前,我们认为这个设计是一致的,并且根据我们选择创建这个令牌的算法,我们相信这是一种安全的方法。
我们的疑问是:
- 我们是否在重新发明轮子,而不使用带有某种自定义提供程序的HTTP身份验证?如何做到这一点?
- Spring Security能帮助我们吗?我们已经阅读了一些相关文章,但不清楚是否可以在非Spring应用程序中使用它;
- 我们是否没有考虑到任何安全漏洞?
提前感谢。任何帮助都将不胜感激。
Filipe
Spring security解决了问题描述,并且作为额外的奖励,你将免费获得所有Spring security的功能。
令牌方法很棒,以下是如何使用spring-security保护你的API的方法 实现AuthenticationEntryPoint并将commence方法设置为401而不是重定向3XX,如下所示
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,"拒绝访问");
@Override
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) // 因此是无状态的 .and() .exceptionHandling() .authenticationEntryPoint(unauthorizedHandler) // 注意入口点 .and() .addFilter(authTokenProcessingFilter) // 注意过滤器 .authorizeRequests() .antMatchers("/resources/**", "/api/authenticate").permitAll() //.antMatchers("/admin/**").hasRole("ADMIN") //.antMatchers("/providers/**").hasRole("ADMIN") .antMatchers("/persons").authenticated(); } }
-- 最后,你需要另一个认证和令牌生成的端点 这是一个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 { 2023-09-16T20:03:25 回复