有時候,我們用到Spring Security驗證框架的時候需要用到多個參數,因爲默認提供了2個參數username和password,加入我們需要curLoginType這個參數怎麼設置
1.重寫UsernamePasswordAuthenticationToken這個類,由於這個類已經有spring封裝,我們把源代碼改一下,重新命名爲MyAuthenticationToken,代碼如下:
package com.zhengqing.config.security.filter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
import java.util.Map;
public class MyAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 420L;
private final Object principal;
private Object credentials;
/**
* 參數map
*/
private Map<String,Object> map;
public MyAuthenticationToken(Object principal, Object credentials) {
super((Collection)null);
this.principal = principal;
this.credentials = credentials;
this.setAuthenticated(false);
}
public MyAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities, Map<String,Object> map){
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
this.map=map;
}
public Object getCredentials() {
return this.credentials;
}
public Object getPrincipal() {
return this.principal;
}
public Map<String,Object> getParam(){
return map;
}
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
} else {
super.setAuthenticated(false);
}
}
public void eraseCredentials() {
super.eraseCredentials();
this.credentials = null;
}
}
注意:我改了代碼加了一個屬性Map,成爲參數Map
/**
* 參數map
*/
private Map<String,Object> map;
2.將自定義用戶名密碼過濾器中的 UsernamePasswordAuthenticationToken替換成自己的MyAuthenticationToken 即可:
package com.zhengqing.config.security.filter;
import com.alibaba.fastjson.JSONObject;
import com.zhengqing.config.Constants;
import com.zhengqing.config.security.login.CusAuthenticationManager;
import com.zhengqing.utils.MultiReadHttpServletRequest;
import com.zhengqing.config.security.login.AdminAuthenticationFailureHandler;
import com.zhengqing.config.security.login.AdminAuthenticationSuccessHandler;
import com.zhengqing.modules.system.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* <p> 自定義用戶密碼校驗過濾器 </p>
*
* @author : zhengqing
* @description :
* @date : 2019/10/12 15:32
*/
@Slf4j
@Component
public class AdminAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
/**
* @param authenticationManager: 認證管理器
* @param adminAuthenticationSuccessHandler: 認證成功處理
* @param adminAuthenticationFailureHandler: 認證失敗處理
*/
public AdminAuthenticationProcessingFilter(CusAuthenticationManager authenticationManager, AdminAuthenticationSuccessHandler adminAuthenticationSuccessHandler, AdminAuthenticationFailureHandler adminAuthenticationFailureHandler) {
super(new AntPathRequestMatcher("/login", "POST"));
this.setAuthenticationManager(authenticationManager);
this.setAuthenticationSuccessHandler(adminAuthenticationSuccessHandler);
this.setAuthenticationFailureHandler(adminAuthenticationFailureHandler);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (request.getContentType() == null || !request.getContentType().contains(Constants.REQUEST_HEADERS_CONTENT_TYPE)) {
throw new AuthenticationServiceException("請求頭類型不支持: " + request.getContentType());
}
MyAuthenticationToken authRequest;
try {
MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(request);
// 將前端傳遞的數據轉換成jsonBean數據格式
User user = JSONObject.parseObject(wrappedRequest.getBodyJsonStrByJson(wrappedRequest), User.class);
String curLoginType=user.getCurLoginType();
Map<String,Object> map=new HashMap<>();
map.put("curLoginType",curLoginType);
authRequest = new MyAuthenticationToken(user.getUsername(), user.getPassword(), null,map);
authRequest.setDetails(authenticationDetailsSource.buildDetails(wrappedRequest));
} catch (Exception e) {
throw new AuthenticationServiceException(e.getMessage());
}
Authentication ac =this.getAuthenticationManager().authenticate(authRequest);
return ac;
}
}
注意:
// 將前端傳遞的數據轉換成jsonBean數據格式
User user = JSONObject.parseObject(wrappedRequest.getBodyJsonStrByJson(wrappedRequest), User.class);
這個user對象是用戶自定義的,我這裏加了一個參數爲curLoginType,前端curLoginType會自動封裝到這個user裏面,
後面這段代碼
String curLoginType=user.getCurLoginType();
Map<String,Object> map=new HashMap<>();
map.put("curLoginType",curLoginType);
authRequest = new MyAuthenticationToken(user.getUsername(), user.getPassword(), null,map);
就將參數封裝到map中去了,這個map就在我的MyAuthenticationToken 這個類的參數map中
3.取值
package com.zhengqing.config.security.login;
import com.alibaba.fastjson.JSONObject;
import com.zhengqing.config.Constants;
import com.zhengqing.config.MyProperties;
import com.zhengqing.config.security.dto.SecurityUser;
import com.zhengqing.config.security.filter.MyAuthenticationToken;
import com.zhengqing.config.security.service.impl.UserDetailsServiceImpl;
import com.zhengqing.modules.system.entity.User;
import com.zhengqing.modules.system.mapper.UserMapper;
import com.zhengqing.utils.ApplicationContextUtil;
import com.zhengqing.utils.MultiReadHttpServletRequest;
import com.zhengqing.utils.PasswordUtils;
import com.zhengqing.utils.RedisUtils;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* <p> 自定義認證處理 </p>
*
* @description :
* @author : zhengqing
* @date : 2019/10/12 14:49
*/
@Component
@Slf4j
public class AdminAuthenticationProvider implements AuthenticationProvider {
@Autowired
UserDetailsServiceImpl userDetailsService;
@Autowired
private UserMapper userMapper;
@Autowired
MyProperties MyProperties;
@Autowired
RedisUtils redisUtils;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
MyAuthenticationToken authRequest=(MyAuthenticationToken)authentication;
String curLoginType= MapUtils.getString(authRequest.getParam(),"curLoginType","-1");
// 獲取前端表單中輸入後返回的用戶名、密碼
String userName = (String) authentication.getPrincipal();
String password = (String) authentication.getCredentials();
SecurityUser userInfo = (SecurityUser) userDetailsService.loadUserByUsername(userName);
boolean isValid = PasswordUtils.isValidPassword(password, userInfo.getPassword(), userInfo.getCurrentUserInfo().getSalt());
// 驗證密碼
if (!isValid) {
throw new BadCredentialsException("密碼錯誤!");
}
// 前後端分離情況下 處理邏輯...
// 更新登錄令牌
String token = PasswordUtils.encodePassword(String.valueOf(System.currentTimeMillis()), Constants.SALT);
//將token更新到數據庫中,每次登陸進去要將redis中的token清空,實現擠兌登錄
User user = userMapper.selectById(userInfo.getCurrentUserInfo().getId());
String oldToken=user.getToken();
user.setToken(token);
userMapper.updateById(user);
// 當前用戶所擁有角色代碼
String roleCodes = userInfo.getRoleCodes();
// 生成jwt訪問令牌
/*String jwt = Jwts.builder()
// 用戶角色
.claim(Constants.ROLE_LOGIN, roleCodes)
// 主題 - 存用戶名
.setSubject(authentication.getName())
// 過期時間
.setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * MyProperties.getAuth().getTokenExpireTime()))
// 加密算法和密鑰
.signWith(SignatureAlgorithm.HS512, Constants.SALT)
.compact();*/
Map<String,Object> redisMap=new HashMap<>();
//將用戶信息存入redis
redisMap.put(Constants.REDIS_KEY,userInfo);
log.info("jwt有效時間爲: "+MyProperties.getAuth().getTokenExpireTime());
//token作爲鍵存入redis
redisUtils.redisTimeSet(token,MyProperties.getAuth().getTokenExpireTime(),redisMap);
//登錄成功後將原來的token清空
redisUtils.redisDel(oldToken);
userInfo.getCurrentUserInfo().setToken(token);
MyAuthenticationToken result = new MyAuthenticationToken(userInfo, userInfo.getAuthorities());
//return new UsernamePasswordAuthenticationToken(userInfo, password, userInfo.getAuthorities());
return result;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
注意
MyAuthenticationToken authRequest=(MyAuthenticationToken)authentication;
String curLoginType= MapUtils.getString(authRequest.getParam(),"curLoginType","-1");
通過強制轉換就可以得到我自定義的那個類MyAuthenticationToken,就可以找到這個參數map了,很巧妙的解決了多個參數的傳遞問題
注意,本例有很多與本話題無關的代碼,只需要細讀關鍵代碼即可!
附上前端postman傳參:
{"username":"admin2","password":"123456","curLoginType":"1"}