四、优化
优化点如下:
- 兼容性问题:
- 因为既要兼容原始登陆模块,又要兼容新建的管理员模块,所以需要判断是原始用户还是管理员用户。
- 验证token时需要判断是认证服务颁发的令牌还是老登陆系统颁发的令牌并分别验证。
- 分布式问题:ExceptionTransactionFilter中将request请求信息保存到session中,如果是分布式部署,会有访问不到session的问题发生。
- 异常返回问题:资源服务器自定义异常返回。
4.1 兼容性优化
生成令牌
逻辑
- 音箱作为第三方需要通过授权码模式获取令牌,携带令牌访问资源(用户信息在原始的登陆模块中),校验账号密码时还需要分两种情况如下
- 密码登陆
- 验证码登陆
- 管理员需要通过密码模式获取令牌(管理员信息在新建的内部用户模块中)
关键点
需要判断账号密码属于原始用户的还是管理员的,当然后端无法判断,所以可以让登录页面发送请求时主动携带标识位,后端通过该标识位来判断。如内部管理系统登陆页面,在页面代码中发送请求时多带上一个标识位参数。
代码
先写一个枚举类记录所有的类型
public enum UserTypeEnum {
ADMIN(1,"admin","管理员登录"),
HIFUN_USERNAME(2,"hifun_username","用户账号密码登录"),
HIFUN_PHONE(3,"hifun_phone","用户手机验证码登录");
Integer code;
String codeText;
String description;
UserTypeEnum(Integer code, String codeText, String description) {
this.code = code;
this.codeText = codeText;
this.description = description;
}
public Integer getCode() {
return code;
}
public String getCodeText() {
return codeText;
}
public String getDescription(){
return description;
}
}
注:手机验证码是通过第三方平台接口来做的,用户相关的登陆最终都会调用原有的用户系统的接口进行验证。
通过Feign来调用用户系统的接口
@FeignClient(name = "hifun-service-user")
public interface HifunFeign {
/**
* 验证密码
*
* @param id
* @param password
* @return
*/
@PostMapping(path = "/password/check", consumes = "application/json")
String check(@RequestParam("id") Integer id, @RequestBody String password);
/**
* 根据手机号获取用户信息
*
* @param mobile
* @return
*/
@GetMapping(path = "/user/mobile")
String mobile(@RequestParam("mobile") String mobile);
/**
* 极验初始化接口
*
* @param clientType
* @param ip
* @param smsType
* @return
*/
@GetMapping(path = "/geetest")
String geetest(@RequestParam("clientType") String clientType, @RequestParam("ip") Integer ip, @RequestParam("smsType") String smsType);
/**
* 极验二次验证并发送短信验证码
*
* @param geetestPO
* @return
*/
@PostMapping(path = "/geetest")
String geetest(@RequestBody GeetestPO geetestPO);
/**
* 校验手机和验证码是否正确
*
* @return
*/
@PostMapping(path = "/login/code")
String code(@RequestBody CodePO codePO);
@PostMapping(path = "/password/findPassword")
void findPassword(@RequestBody PasswordPO passwordPO);
}
重写MyUserDetailsServer类中的loadUserByUsername方法
@Service
@Slf4j
public class MyUserDetailsServer implements UserDetailsService {
@Resource
private HifunFeign hifunFeign;
@Resource
private UserCenterFeign userCenterFeign;
@Resource
private HttpServletRequest httpServletRequest;
/**
* 将账号密码和权限信息封装到UserDetails对象中返回
*
* @param username
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// String userType = httpServletRequest.getHeader("User-Type");
String userType = httpServletRequest.getParameter("user_type");
System.out.println("*****loadUserByUsername userType:" + userType);
//查询数据库获取用户的信息
if (UserTypeEnum.ADMIN.getCodeText().equals(userType)) {
// 1.请求头是admin,查询到管理人员数据库
TbUserPO tbUser = userCenterFeign.getTbUser(username);
Assert.isTrue(!Objects.isNull(tbUser), "管理员不存在");
//将用户信息添加到token中
// UserInfoDTO userInfo = BeanUtil.copyProperties(tbUser, UserInfoDTO.class);
// JSONObject userObj = new JSONObject(userInfo);
// String userStr = userObj.toString();
tbUser.setUsername(tbUser.getId().toString());
//获取用户的角色和权限
List<String> roleCodes = userCenterFeign.getRoleCodes(username);
List<String> authorities = userCenterFeign.getAuthorities(roleCodes);
//将用户角色添加到用户权限中
authorities.addAll(roleCodes);
//设置UserDetails中的authorities属性,需要将String类型转换为GrantedAuthority
MyUserDetails myUserDetails = BeanUtil.copyProperties(tbUser, MyUserDetails.class);
myUserDetails.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(String.join(",", authorities)));
log.info("UserDetail:" + myUserDetails);
return myUserDetails;
} else if (UserTypeEnum.HIFUN_PHONE.getCodeText().equals(userType)) {
// 2.请求头是hifun_phone,查询火粉的用户中心
// String userInfo;
// try {
// userInfo = hifunFeign.mobile(username);
// } catch (Exception e) {
// throw new IllegalArgumentException("该用户不存在");
// }
// JSONObject jsonObject = new JSONObject(userInfo);
// Integer id = jsonObject.getInt("id");
return new MyUserDetails().setUsername(username).setPassword("hifun")
.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(String.join(",", "hifun")))
.setAccountNonExpired(true)
.setAccountNonLocked(true)
.setCredentialsNonExpired(true)
.setEnabled(true);
//这个User对象是校验client的账号密码时使用的,expire、lock等信息自动填充为true
// return new User(id.toString(), "hifun", AuthorityUtils.commaSeparatedStringToAuthorityList(String.join(",", "hifun")));
} else if (UserTypeEnum.HIFUN_USERNAME.getCodeText().equals(userType)) {
String userInfo;
try {
userInfo = hifunFeign.mobile(username);
} catch (Exception e) {
throw new IllegalArgumentException("该用户不存在");
}
JSONObject jsonObject = new JSONObject(userInfo);
Integer id = jsonObject.getInt("id");
return new User(id.toString(), "hifun", AuthorityUtils.commaSeparatedStringToAuthorityList(String.join(",", "hifun")));
}
// else {
// return new User("test", "hifun", AuthorityUtils.commaSeparatedStringToAuthorityList(String.join(",", "hifun")));
// }
throw new IllegalArgumentException("未传递用户类型或用户类型不存在");
}
}
重写DaoAuthenticationProvider类中的additionalAuthenticationChecks方法
@Component
public class MyDaoAuthenticationProvider extends DaoAuthenticationProvider {
@Resource
private HifunFeign hifunFeign;
@Resource
private BCryptPasswordEncoder bCryptPasswordEncoder;
public MyDaoAuthenticationProvider(UserDetailsService userDetailsService) {
super();
// 这个地方一定要对userDetailsService赋值,不然userDetailsService是null
setUserDetailsService(userDetailsService);
}
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
assert requestAttributes != null;
HttpServletRequest httpServletRequest = requestAttributes.getRequest();
HttpServletResponse httpServletResponse = requestAttributes.getResponse();
String presentedPassword = authentication.getCredentials().toString();
// Cookie[] cookies = httpServletRequest.getCookies();
// for (Cookie cookie : cookies) {
// System.out.println("*****cookie:" + cookie.getName());
// cookie.setMaxAge(0);
// cookie.setPath("/");
// assert httpServletResponse != null;
// httpServletResponse.addCookie(cookie);
// }
String userType = httpServletRequest.getParameter("user_type");
System.out.println("*****additionalAuthenticationChecks userType:" + userType);
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
// TODO 根据请求头的用户类型进行查询
if (UserTypeEnum.ADMIN.getCodeText().equals(userType)) {
// 1.请求头是admin,查询到管理人员数据库
System.out.println("user_type是admin,查询到管理人员数据库");
if (!bCryptPasswordEncoder.matches(presentedPassword, userDetails.getPassword())) {
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
} else if (UserTypeEnum.HIFUN_PHONE.getCodeText().equals(userType)) {
// 2.请求头是hifun_phone,校验手机验证码是否正确
System.out.println("user_type是hifun_phone,校验手机验证码是否正确");
String phone = userDetails.getUsername();
CodePO codePO = new CodePO(httpServletRequest.getParameter("app")
, (long) IpUtils.getLongIp(), phone, ""
, Long.valueOf(httpServletRequest.getParameter("terminal"))
, httpServletRequest.getParameter("uuid"), presentedPassword);
System.out.println("*****additionalAuthenticationChecks codePO:" + codePO);
try {
String code = hifunFeign.code(codePO);
System.out.println("*****additionalAuthenticationChecks code:" + code);
} catch (Exception e) {
logger.debug("Authentication failed: password does not match stored value");
try {
httpServletRequest.setAttribute("message", e.getMessage());
httpServletRequest.getRequestDispatcher("/uaa/error").forward(httpServletRequest, httpServletResponse);
} catch (Exception ex) {
e.printStackTrace();
throw new IllegalArgumentException(e.getMessage());
}
throw new IllegalArgumentException("停止程序直接返回结果");
// throw new BadCredentialsException(messages.getMessage(
// "AbstractUserDetailsAuthenticationProvider.badCredentials",
// "Bad credentials"));
}
} else if (UserTypeEnum.HIFUN_USERNAME.getCodeText().equals(userType)) {
// 3.请求头是hifun_username,校验手机密码是否正确
System.out.println("user_type是hifun_username,校验手机密码是否正确");
HashMap<Object, Object> map = new HashMap<>();
map.put("password", presentedPassword);
System.out.println("*****additionalAuthenticationChecks id:" + userDetails.getUsername() + " --- password:" + presentedPassword);
String check = hifunFeign.check(Integer.parseInt(userDetails.getUsername()), new JSONObject(map).toString());
JSONObject jsonObject = new JSONObject(check);
JSONObject data = jsonObject.getJSONObject("data");
Integer success = data.getInt("success");
if (success == 0) {
logger.debug("Authentication failed: password does not match stored value");
try {
httpServletRequest.setAttribute("message", "账号密码错误!");
httpServletRequest.getRequestDispatcher("/uaa/error").forward(httpServletRequest, httpServletResponse);
} catch (Exception e) {
e.printStackTrace();
throw new IllegalArgumentException(e.getMessage());
}
throw new IllegalArgumentException("停止程序直接返回结果");
// throw new BadCredentialsException(messages.getMessage(
// "AbstractUserDetailsAuthenticationProvider.badCredentials",
// "Bad credentials"));
}
}
String type = httpServletRequest.getHeader("type");
if ("option".equals(type)) {
System.out.println("*****type:" + type);
try {
httpServletRequest.setAttribute("message", "验证成功!");
httpServletRequest.getRequestDispatcher("/uaa/success").forward(httpServletRequest, httpServletResponse);
throw new IllegalArgumentException("停止程序直接返回结果");
} catch (Exception e) {
e.printStackTrace();
throw new IllegalArgumentException(e.getMessage());
}
// } else {
// Cookie[] cookies = httpServletRequest.getCookies();
// for (Cookie cookie : cookies) {
// System.out.println("*****cookie:" + cookie.getName());
// cookie.setMaxAge(0);
// cookie.setPath("/");
// assert httpServletResponse != null;
// httpServletResponse.addCookie(cookie);
// }
// Cookie session = new Cookie("SESSION", null);
// s