在我的用戶密碼授權文章里介紹了spring-security的工作過程,不了解的同學,可以先看看用戶密碼授權這篇文章,在
用戶密碼授權模式里,主要是通過一個登陸頁進行授權,然后把授權對象寫到session里,它主要用在mvc框架里,而對於webapi來說,一般不會采用這種方式,對於webapi
來說,一般會用jwt授權方式,就是token授權碼的方式,每訪問api接口時,在http頭上帶着你的token碼,而大叔自己也寫了一個簡單的jwt授權模式,下面介紹一下。
WebSecurityConfig授權配置
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* token過濾器.
*/
@Autowired
LindTokenAuthenticationFilter lindTokenAuthenticationFilter;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
// 基於token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
// 對於獲取token的rest api要允許匿名訪問
.antMatchers("/lind-auth/**").permitAll()
// 除上面外的所有請求全部需要鑒權認證
.anyRequest().authenticated();
httpSecurity
.addFilterBefore(lindTokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
// 禁用緩存
httpSecurity.headers().cacheControl();
}
/**
* 密碼生成策略.
*
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
授權接口login
對外開放的,需要提供用戶名和密碼為參數進行登陸,然后返回token碼,當然也可以使用手機號和驗證碼登陸,授權邏輯是一樣的,獲取用戶信息都是使用UserDetailsService
,
然后開發人員根據自己的業務去重寫loadUserByUsername
來獲取用戶實體。
用戶登陸成功后,為它授權及認證,這一步我們會在redis里建立token與用戶名的關系。
@GetMapping(LOGIN)
public ResponseEntity<?> refreshAndGetAuthenticationToken(
@RequestParam String username,
@RequestParam String password) throws AuthenticationException {
return ResponseEntity.ok(generateToken(username, password));
}
/**
* 登陸與授權.
*
* @param username .
* @param password .
* @return
*/
private String generateToken(String username, String password) {
UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken(username, password);
// Perform the security
final Authentication authentication = authenticationManager.authenticate(upToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
// Reload password post-security so we can generate token
final UserDetails userDetails = userDetailsService.loadUserByUsername(username);
// 持久化的redis
String token = CommonUtils.encrypt(userDetails.getUsername());
redisTemplate.opsForValue().set(token, userDetails.getUsername());
return token;
}
LindTokenAuthenticationFilter代碼
主要實現了對請求的攔截,獲取http頭上的Authorization
元素,token碼就在這個鍵里,我們的token都是采用通用的Bearer
開頭,當你的token沒有過期時,會
存儲在redis里,key就是用戶名的md5碼,而value就是用戶名,當拿到token之后去數據庫或者緩存里拿用戶信息進行授權即可。
/**
* token filter bean.
*/
@Component
public class LindTokenAuthenticationFilter extends OncePerRequestFilter {
@Autowired
RedisTemplate<String, String> redisTemplate;
String tokenHead = "Bearer ";
String tokenHeader = "Authorization";
@Autowired
private UserDetailsService userDetailsService;
/**
* token filter.
*
* @param request .
* @param response .
* @param filterChain .
*/
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String authHeader = request.getHeader(this.tokenHeader);
if (authHeader != null && authHeader.startsWith(tokenHead)) {
final String authToken = authHeader.substring(tokenHead.length()); // The part after "Bearer "
if (authToken != null && redisTemplate.hasKey(authToken)) {
String username = redisTemplate.opsForValue().get(authToken);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
//可以校驗token和username是否有效,目前由於token對應username存在redis,都以默認都是有效的
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(
request));
logger.info("authenticated user " + username + ", setting security context");
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
filterChain.doFilter(request, response);
}
測試token授權
get:http://localhost:8080/lind-demo/login?username=admin&password=123
post:http://localhost:8080/lind-demo/user/add
Content-Type:application/json
Authorization:Bearer 21232F297A57A5A743894A0E4A801FC3