Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

spring - Authentication and authorization in REST Services with Liferay

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.

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.: note that although we are using Liferay, it could be any other Java based application instead.

What we have designed is:

  1. When the user logs in in our portal, Liferay creates an authentication token with userLogin (or ID) + client IP + timestamp. This token is saved in a cookie;
  2. Before every REST call, Angular reads this cookie and sends its contents via a HTTP header;
  3. Our service "decrypts" the cookie content sent and verifies if the timestamp is valid, the IP is the same and, according to our business rules, if the user has access to do or read whatever he wants to.

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.

Our doubts are:

  • Are we, somehow, reinventing the wheel not using HTTP authentication with some kind of custom provider? How to?
  • Could Spring Security help us with that? We have read some articles about it but it's not clear if it makes sense to use it with a non-Spring application;
  • Are there any security flaws we have not considered with this approach?

Thank you in advance. Any help is appreciated.

Filipe

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Spring security solves the problem description, and as a bonus you will get all the spring security features for free.

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");
  • Have a TokenProcessingFilter extend and leverage what UsernamePasswordAuthenticationFilter has to offer, override the doFilter() method, extract the the token from the request headers, validate and Authenticate the token as follows

@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);
            }

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();
        }

}

-- 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 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.

  • Hope this helps

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...