LoginController類
具體代碼
/**
* app 登錄
*/
@AnonymousAccess
@PostMapping("login")
public AjaxResult login(@RequestBody LoginBody loginBody) {
AjaxResult ajax = AjaxResult.success();
// 生成令牌
String token = loginService.login(loginBody.getUsername(), loginBody.getPassword());
ajax.put(Constants.TOKEN, token);
return ajax;
}
登錄校驗(yàn) ——AppLoginService類
?具體代碼
@Resource
private AppAuthenticationProvider authenticationManager;
/**
* 登錄驗(yàn)證
*
* @param username 用戶名
* @param password 密碼
* @return 結(jié)果
*/
public String login(String username, String password) {
// 用戶驗(yàn)證
Authentication authentication;
try {
// 該方法會去調(diào)用UserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} 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());
}
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
recordLoginInfo(loginUser.getUserId());
// 生成token
return tokenService.createToken(loginUser);
}
AppAuthenticationProvider 類
?具體代碼
@Component
public class AppAuthenticationProvider implements AuthenticationProvider {
private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
@Autowired
private AppUserDetailsServiceImpl userDetailsService;
@SneakyThrows
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String userName = authentication.getName();// 這個獲取表單輸入中返回的用戶名;
Object password = authentication.getCredentials();//這個獲取表單輸入中返回的密碼;
// 這里構(gòu)建來判斷用戶是否存在和密碼是否正確
UserDetails userInfo = userDetailsService.loadUserByUsername(userName); // 這里調(diào)用我們的自己寫的獲取用戶的方法;
if(!SecurityUtils.matchesPassword(password.toString(),userInfo.getPassword())){
log.info("用戶不存在/密碼錯誤,{}", userName);
throw new ServiceException("用戶不存在/密碼錯誤");
}
Collection<? extends GrantedAuthority> authorities = userInfo.getAuthorities();
// 構(gòu)建返回的用戶登錄成功的token
return new UsernamePasswordAuthenticationToken(userInfo, userInfo.getPassword(), authorities);
}
@Override
public boolean supports(Class<?> authentication) {
// return authentication.equals(UsernamePasswordAuthenticationToken.class);
// 這里直接改成 return true;表示是支持這個執(zhí)行
return true;
}
}
AppUserDetailsServiceImpl類
?具體代碼
@Service
public class AppUserDetailsServiceImpl implements UserDetailsService {
private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
@Autowired
private IProductMemberService memberService;//自己寫的接口
@Autowired
private IProductMemberCourtService memberCourtService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
ProductMember member = memberService.selectUserByUserName(username);//驗(yàn)證登錄用戶
if (StringUtils.isNull(member)) {
log.info("登錄用戶:{} 不存在.", username);
throw new ServiceException("登錄用戶:" + username + " 不存在");
} else if (UserStatus.DELETED.getCode().equals(member.getDelFlag())) {
log.info("登錄用戶:{} 已被刪除.", username);
throw new ServiceException("對不起,您的賬號:" + username + " 已被刪除");
} else if (UserStatus.DISABLE.getCode().equals(member.getStatus())) {
log.info("登錄用戶:{} 已被停用.", username);
throw new ServiceException("對不起,您的賬號:" + username + " 已停用");
}
return createLoginUser(member);
}
public UserDetails createLoginUser(ProductMember member) {
return new LoginUser(member.getMemberId(), memberCourtService.selectCourtIdByMemberId(member.getMemberId()), member);
}
}
此時運(yùn)行時,會有沖突?。?!
需要在 xxx-framework/src/main/java/....../SecurityConfig中條件
@Qualifier("userDetailsServiceImpl")
如圖:
?此時啟動項(xiàng)目不會報(bào)沖突的錯
千萬千萬要添加!??!
下圖中的LongUser類要添加?xùn)|西
?要在public String getPassword(){}中添加自己寫的登錄實(shí)體類的getPassword()
以上內(nèi)容已經(jīng)可以解決app和小程序新的登錄接口方案和后臺管理的登陸獲取token不沖突
以下內(nèi)容可作為參考:
登錄認(rèn)證JWTtoken驗(yàn)證機(jī)制
后端部分
/login 接口
userName
password
code 驗(yàn)證碼
前端獲取上面三個要素后調(diào)用接口,整體改接口做了下面幾件事情
1、驗(yàn)證用戶身份(賬號密碼+驗(yàn)證碼)
2、生成token
3、保存用戶登錄態(tài)到spring security中
安全配置:定義了基本的配置信息
framework.config.SecurityConfig
UserDetailsServiceImpl 用戶驗(yàn)證處理類
登錄接口的服務(wù)類
framework.web.service.SysLoginService
JWT攔截器,攔截令牌并校驗(yàn)信息
framework.security.filter.JwtAuthenticationTokenFilter
詳細(xì)過程
1、SysLoginService 中調(diào)用UserDetailsServiceImpl校驗(yàn)用戶的密碼是否匹配以及用戶賬戶狀態(tài),校驗(yàn)通過后返回UserDetails實(shí)例,該實(shí)例包含了用戶的基本信息和菜單權(quán)限信息
2、調(diào)用tokenService.createToken(loginUser)生成token
令牌生成的詳細(xì)過程
生成uuid隨機(jī)數(shù),這個隨機(jī)數(shù)用來做rediskey存儲token
生成一個token(無時效)
攔截到的token如果距離失效在10分鐘以內(nèi)(可配置)就自動刷新有效期
前面提到了token本身無時效,有效期是通過redis控制的,因?yàn)閖wt本身未提供刷新有效期的方法(可能是我不知道)。
以上用戶調(diào)用了login接口并且獲得了token
jwt令牌校驗(yàn)
?
/**
?* token過濾器 驗(yàn)證token有效性
?*?
?* @author sj
?*/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
{
? ? @Autowired
? ? private TokenService tokenService;
? ? @Override
? ? protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
? ? ? ? ? ? throws ServletException, IOException
? ? {
? ? ? ? LoginUser loginUser = tokenService.getLoginUser(request);
? ? ? ? if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication()))
? ? ? ? {
? ? ? ? ? ? tokenService.verifyToken(loginUser);
? ? ? ? ? ? UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
? ? ? ? ? ? authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
? ? ? ? ? ? SecurityContextHolder.getContext().setAuthentication(authenticationToken);
? ? ? ? }
? ? ? ? chain.doFilter(request, response);
? ? }
}
代碼比較短,所以就直接貼出來,這段代碼攔截了所有請求并且完成了令牌的校驗(yàn)和刷新,具體過程如下
1、tokenService.getLoginUser(request); 從request中獲取token并校驗(yàn),如果校驗(yàn)通過就返回LoginUser對象
2、校驗(yàn)LoginUser的token,如果再刷限期內(nèi)就直接刷新
3、將LoginUser封裝到SecurityContextHolder中作為全局的用戶登錄狀態(tài)
注:第3條有兩個好處
1、后續(xù)攔截器發(fā)現(xiàn)SecurityContextHolder中保存了用戶時,就直接通過校驗(yàn)
2、通過SecurityContextHolder可以快速獲取當(dāng)前請求的登錄信息。
以上基本上已經(jīng)說名了JWT校驗(yàn)的基本過程,忽略了很多細(xì)節(jié)
getInfo 獲取用戶信息
1、用戶的基本信息
2、用戶所有的Permissions(菜單樹)
3、用戶所有的RopePersmission(roleKeys)
getRouters 獲取前端頁面路由信息
這個接口完全為前端準(zhǔn)備,后面會專門講述前端的權(quán)限控制文章來源:http://www.zghlxwxcb.cn/news/detail-729530.html
?文章來源地址http://www.zghlxwxcb.cn/news/detail-729530.html
到了這里,關(guān)于若依前后端分離版:增加新的登錄接口,用于小程序或者APP獲取token,并使用若依的驗(yàn)證方法的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!