使用 Liferay 在 REST 服务中进行身份验证和授权 [英] Authentication and authorization in REST Services with Liferay
问题描述
我们正在构建一些将通过 RESTful API 公开的服务.此 API 的主要客户是使用 Angular JS 的 Liferay Portlet,这意味着客户端 (Angular) 可以直接调用我们的服务.
We are building some services that will be exposed through a RESTful API. Primary customers of this API are Liferay portlets using Angular JS, meaning there are direct calls from client-side (Angular) to our services.
到目前为止,我们已经设计了一种身份验证和授权机制,以确保我们可以识别哪个登录用户 (Liferay) 正在请求我们的 API.
So far we have designed an authentication and authorization mechanism to assure that we can identify which logged user (Liferay) is requesting our API.
PS.:请注意,虽然我们使用的是 Liferay,但它可以是任何其他基于 Java 的应用程序.
我们的设计是:
- 当用户登录我们的门户时,Liferay 会创建一个带有 userLogin(或 ID)+ 客户端 IP + 时间戳的身份验证令牌.此令牌保存在 cookie 中;
- 在每次 REST 调用之前,Angular 会读取此 cookie 并通过 HTTP 标头发送其内容;
- 我们的服务会解密"发送的 cookie 内容,并验证时间戳是否有效、IP 是否相同,以及根据我们的业务规则,用户是否有权执行或阅读他想做的任何事情.
这个设计现在对我们来说看起来是一致的,根据我们选择的创建这个令牌的算法,我们相信这是一种安全的方法.
This design looks consistent to us right now and, depending on the algorithm we choose to create this token, we believe it is a secure approach.
我们的疑问是:
- 不知何故,我们是否在重新发明轮子,而不是通过某种自定义提供程序使用 HTTP 身份验证?怎么做?
- Spring Security 能帮我们解决这个问题吗?我们已经阅读了一些关于它的文章,但不清楚在非 Spring 应用程序中使用它是否有意义;
- 是否存在我们没有考虑过这种方法的安全漏洞?
先谢谢你.任何帮助表示赞赏.
Thank you in advance. Any help is appreciated.
菲利普
推荐答案
Spring security 解决了问题描述,作为奖励,您将免费获得所有 Spring Security 功能.
Spring security solves the problem description, and as a bonus you will get all the spring security features for free.
令牌方法很棒,这里是您如何使用 spring-security 保护 API 的方法实现 AuthenticationEntryPoint 并将开始方法设置为 401 而不是重定向 3XX 如下
The Token approach is great and here is how you can secure your APIs with spring-security Implements AuthenticationEntryPoint and have the commence method set 401 instead of re-direction 3XX as follows
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Access Denied");
- 让 TokenProcessingFilter 扩展并利用 UsernamePasswordAuthenticationFilter 必须提供的内容,覆盖 doFilter() 方法,从请求标头中提取令牌,如下验证和验证令牌
@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 配置看起来像
Your Spring-security configuration will look like
@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();
}
}
-- 最后,您将需要另一个用于身份验证和令牌生成的端点这是一个 spring MVC 示例
-- Last you will need another end point for Authentication and token-generation Here is a spring MVC example
@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 生成令牌,2. 后续 API 调用应具有令牌是的,spring-security 可以做到这一点,而且您不必在身份验证、授权方面开辟新天地.
1 Generate token, 2. subsequent API-calls should have the token Yes spring-security can do this and you don`t have to break new grounds in Authentication, Authorization.
- 希望这会有所帮助
这篇关于使用 Liferay 在 REST 服务中进行身份验证和授权的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!