我一直在網上衝浪,看看許多不同的方式來實現基於令牌的認證使用春季&春季安全(SS)。我並不是真的想要在Oauth路線上全力以赴,所以我一直在努力做一些事情,讓事情變得非常簡單。春季安全和令牌認證的API
我想要的是將用戶名/密碼傳遞給SS的內置機制,並在成功時生成令牌並將其傳回給用戶。然後,用戶使用自定義標題中的令牌進行所有未來的請求。令牌將在很長時間後過期。我知道這是Oauth所做的,但又不想使用它。
所以我有一些工作。我可以用用戶名/密碼登錄並取回令牌。然後,我可以成功地用令牌發出請求。什麼是不工作是權威。下面是我在做什麼...
- 自定義驗證失敗處理器簡單地返回
HttpServletResponse.SC_UNAUTHORIZED
- 定製成功處理程序簡單地返回
HttpServletResponse.SC_OK
和令牌 - 自定義驗證入口點只是
HttpServletResponse.SC_UNAUTHORIZED
應答
現在,我也有一個自定義UserDetails和UserDetailsService。
public class MyUserDetails implements UserDetails {
private User user; // this is my own User object
private List<GrantedAuthority> authorities;
public MyUserDetails(User user, List<GrantedAuthority> authorities) {
this.user = user;
this.authorities = authorities;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public void setAuthorities(List<GrantedAuthority> authorities) {
this.authorities = authorities;
}
}
@Service
public class MyUserDetailsService implements UserDetailsService {
private final UserService userService;
@Autowired
public MyUserDetailsService(UserService userService) {
this.userService = userService;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username);
}
List<GrantedAuthority> authorities = new ArrayList<>();
// for now, just add something
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
return new MyUserDetails(user, authorities);
}
}
爲了在標題中查找單詞和告訴春天一切都很好,我創建了一個AuthTokenFilter ...
public class AuthTokenFilter extends UsernamePasswordAuthenticationFilter {
@Autowired
private MyUserDetailsService userDetailsService;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String authToken = httpRequest.getHeader("X-TOKEN-AUTH");
String username = null;
if (authToken != null) {
username = Jwts.parser()
.setSigningKey("1234")
.parseClaimsJws(authToken)
.getBody()
.getSubject();
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
// TODO: validate token
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
}
這就是我是如何配置的Web安全:
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetailsService userDetailsService;
@Autowired
private RestAuthEntryPoint authenticationEntryPoint;
@Autowired
private AuthSuccessHandler authSuccessHandler;
@Autowired
private AuthFailureHandler authFailureHandler;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
@Override
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(new ShaPasswordEncoder());
return authenticationProvider;
}
@Bean
public AuthTokenFilter authenticationTokenFilterBean() throws Exception {
AuthTokenFilter authenticationTokenFilter = new AuthTokenFilter();
authenticationTokenFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationTokenFilter;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authenticationProvider(authenticationProvider())
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.formLogin()
.permitAll()
.loginProcessingUrl("/login")
.usernameParameter("username")
.passwordParameter("password")
.successHandler(authSuccessHandler)
.failureHandler(authFailureHandler)
.and()
.logout()
.permitAll()
.and()
.sessionManagement()
.maximumSessions(1);
http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
http.authorizeRequests().anyRequest().authenticated();
}
}
一切似乎工作接受SS不限制訪問。如果令牌在那裏,SS只是讓一切都通過。