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.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…