若依系统(分离版后端)自带的账号验证是基于
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
验证,然后在系统中controller或service类中 SecurityUtils 工具类中直接可获取用户或用户ID等
SecurityUtils.getLoginUser().getUser();
现在想实现,微信小程序通过code等登录后返回和之前一样的token后原逻辑中的SecurityUtils一样可以获取到,则需要自定义扩展登录类。废话不多说,上代码:
类似于UsernamePasswordAuthenticationToken类,自定义AuthCodeAuthenticationToken.java
在这个位置:
AuthCodeAuthenticationToken.java:
package com.ruoyi.framework;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
public class AuthCodeAuthenticationToken extends AbstractAuthenticationToken {
private final Object principal;
public AuthCodeAuthenticationToken(Object principal) {
super(null);
this.principal= principal;
}
public AuthCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal= principal;
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getPrincipal() {
return this.principal;
}
@Override
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");
}
super.setAuthenticated(false);
}
}
AuthCodeAuthenticationProvider.java
package com.ruoyi.framework;
import org.springframework.security.authentication.AuthenticationProvider;
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;
public class AuthCodeAuthenticationProvider implements AuthenticationProvider {
private UserDetailsService userDetailsService;
public AuthCodeAuthenticationProvider(UserDetailsService userDetailsService) {
setUserDetailsService(userDetailsService);
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
AuthCodeAuthenticationToken authCodeAuthenticationToken = (AuthCodeAuthenticationToken) authentication;
String telephone = (String) authentication.getPrincipal();
UserDetails userDetails = userDetailsService.loadUserByUsername(telephone);
AuthCodeAuthenticationToken authenticationResult = new AuthCodeAuthenticationToken(userDetails, userDetails.getAuthorities());
authenticationResult.setDetails(authentication.getDetails());
return authenticationResult;
}
@Override
public boolean supports(Class<?> authentication) {
return AuthCodeAuthenticationToken.class.isAssignableFrom(authentication);
}
public UserDetailsService getUserDetailsService() {
return userDetailsService;
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
}
在这里还要重写UserDetailsServiceByAuthCodeImpl.java原逻辑是调用的UserDetailsServiceImpl.java中的loadUserByUsername方法。
package com.ruoyi.framework.web.service;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Slf4j
@Service("userDetailsByAuthCode")
public class UserDetailsServiceByAuthCodeImpl implements UserDetailsService {
@Autowired
private ISysUserService userService;
@Autowired
private SysPermissionService permissionService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
SysUser user = userService.selectUserByUserName(username);
if (StringUtils.isNull(user))
{
log.info("登录用户:{} 不存在.", username);
throw new ServiceException(MessageUtils.message("user.not.exists"));
}
else if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
{
log.info("登录用户:{} 已被删除.", username);
throw new ServiceException(MessageUtils.message("user.password.delete"));
}
else if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
{
log.info("登录用户:{} 已被停用.", username);
throw new ServiceException(MessageUtils.message("user.blocked"));
}
return createLoginUser(user);
}
public UserDetails createLoginUser(SysUser user)
{
return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
}
}
在SecurityConfig.java中配置自定义的登录逻辑(顶部注入,下面增加配置)
配置中api开头的接口,该放开(登录获取token的接口)的放开:
温馨提示:一定要区分原来的UserDetailsService 使用注解@Qualifier("userDetailsByAuthCode")区分,原来的也要加上,不然启动时候注入重复,原来的serviceImpl实现类也要加上,如下:
SysLoginService.java中
public String wxLogin(String userName)
{
// 用户验证
Authentication authentication = null;
try
{
AuthCodeAuthenticationToken authCodeAuthenticationToken = new AuthCodeAuthenticationToken(userName);
AuthenticationContextHolder.setContext(authCodeAuthenticationToken);
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager.authenticate(authCodeAuthenticationToken);
}
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();
recordLoginInfo(loginUser.getUserId());
// 生成token
return tokenService.createToken(loginUser);
}
接口调用中可以如下调用了: