Spring Security 後端接受多個參數

有時候,我們用到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"}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章