在項目中,前后端分離的若依項目,需要通過統一認證,或者是第三方協帶認證信息跳轉到本系統的指定頁面。需要前后端都做相應的改造,由于第一次實現時已過了很久,再次重寫時,發現還是搞了很長時間,所以花點時間整理出事,也方便給大家參考。
首先明確需要處理幾個部分:
一、后端的處理
? ? ? ?1、 在控制器層,SysLoginController? 中,原有的login方法之后,寫一個登錄的方法,注意其中的 loginService.logingcy(userId); 這個是下一步要實現的。通過第三方協議解析出用戶的信息,可能 是工號,或者是微信的openid這都無所謂,反正通過他你能找到本系統的用戶就可以了。
@GetMapping("/logingcy")public AjaxResult logingcy(String ssouser){AjaxResult ajax = AjaxResult.success();try {//通過中間的協議解析出用戶的基本信息String[] userInfo = JasmineSsoUtil.getUserInfo(ssouser, Constants.SSODOMAIN, Constants.SSOKEY);String userId = userInfo[0];//以下為獲取此員工的待辦工單數量,并按要求返回值System.out.println("useris "+userId);String token = loginService.logingcy(userId);ajax.put(Constants.TOKEN, token);return ajax;} catch (Exception e) {e.printStackTrace();}return AjaxResult.error();// 生成令牌}
2、在登錄服務中增加一個和原來登錄對應的方法 SysLoginService???loginService.logingcy(userId);
/*通過工程翼用戶登錄*/public String logingcy(String username){// 用戶驗證Authentication authentication = null;SysUser sysUser = userService.selectUserByUserName(username);System.out.println("admin staffcode is "+ username +" and password is "+sysUser.getPassword());try{//已經確認解析出用戶信息,所以這里使用一個內部的特定密碼和模擬密碼進行檢驗userDetailsService.loadUserByUsername(username);UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, Constants.CPASSWORD);//AuthenticationContextHolder.setContext(token);// 該方法會去調用UserDetailsServiceImpl.loadUserByUsernameauthentication = authenticationManager.authenticate(token);}catch (Exception e){if (e instanceof BadCredentialsException){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));throw new UserPasswordNotMatchException();}else{AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));throw new ServiceException(e.getMessage());}}finally{// AuthenticationContextHolder.clearContext();}AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));LoginUser loginUser = (LoginUser) authentication.getPrincipal();// 生成tokenreturn tokenService.createToken(loginUser);}
3、增加一個 SecurityProvider 用于密碼和校檢
具體的代碼如下:
package com.ruoyi.framework.security.provider;import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.system.service.ISysUserService;
import org.apache.poi.hssf.record.DVALRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;/*** 自定義認證服務* */
@Service("securityProvider")
public class SecurityProvider implements AuthenticationProvider {@Autowiredprivate BCryptPasswordEncoder bCryptPasswordEncoder;@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate ISysUserService userService;public SecurityProvider(UserDetailsService userDetailsService) {this.userDetailsService = userDetailsService;}@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {/* UsernamePasswordAuthenticationToken token= (UsernamePasswordAuthenticationToken) authentication;*/String name = authentication.getName();String password = (String) authentication.getCredentials();System.out.println("name:"+name+"password:"+password);//這里直接判斷異常UserDetails user = userDetailsService.loadUserByUsername(name);String encoderPassword = bCryptPasswordEncoder.encode(password);SysUser user1 = userService.selectUserByUserName("admin");user1.setPassword(SecurityUtils.encryptPassword("admin123"));userService.updateUser(user1);System.out.println("1password is "+ SecurityUtils.encryptPassword(password));System.out.println("2password is"+encoderPassword);System.out.println("user passwordis "+user.getPassword());// 數據庫賬號密碼的校驗能通過就通過if (SecurityUtils.matchesPassword(password, user.getPassword())){// if (bCryptPasswordEncoder.matches(password, user.getPassword())) {// log.info("使用賬戶密碼登錄");return new UsernamePasswordAuthenticationToken(user, encoderPassword);}//這里是第三方登錄的使用方式,用一個內部的密碼代替了password的位置// log.info(""+checkValid);if(Constants.CPASSWORD.equalsIgnoreCase(password)){return new UsernamePasswordAuthenticationToken(user, password);} else {// 如果都登錄不了,就返回異常輸出throw new UserPasswordNotMatchException();}}@Overridepublic boolean supports(Class<?> authentication) {//返回true后才會執行上面的authenticate方法,這步能確保authentication能正確轉換類型return true;}}
4、修改配置文件? SecurityConfig 有兩處需要修改,一個是最下面,使用上一步定義的SecurityProvider
一個是上面,允許前端的新登錄頁面可以直接訪問
到此,后端部分的改造就完成了。
二、前端頁面
1、首先要做一個登錄過度頁面,內容可以為空白,只需要接收地址欄中的參數,并且調用上面第一步中定義的方法
2、在路由中配置路徑對應訪問的這個頁面
3、設置白名單,可以直接訪問頁面
4、在原來的login.js中寫入后臺對應的地址,這里注意需要先logou一下,防止重復登錄報錯
export function logingcy(ssouser) {logout();return request({url: '/logingcy?ssouser=' + ssouser,method: 'get',})
}
5、在user.js中增加一個action,這里需要寫入token是關鍵,不然后臺一直報未登錄