一、通过用户名、密码登陆 二、通过短信验证码登陆
一、通过用户名、密码登陆
@PostMapping("/login")
@ApiOperation(value = "移动助手登陆用户")
public Object login(@ApiParam(required = true, value = "登录帐号和密码") @Validated @RequestBody VisitLoginReqDTO request,
HttpServletRequest httpServletRequest) {
String clientIp = WebUtil.getHost(httpServletRequest);
if (null != SecurityUtils.getSubject() && SecurityUtils.getSubject().isAuthenticated()) {
try {
SecurityUtils.getSubject().logout();
} catch (Exception e) {
}
}
try {
if (login(request.getLoginId(), SecurityUtil.encryptPassword(request.getPassword()), clientIp)) {
request.setLastLoginIp(clientIp);
request.setLastLoginTime(LocalDateTime.now());
LoginReqDTO loginReqDTO = request.convertTo(LoginReqDTO.class);
BaseResponse<Long> resp = systemUserFacade.updateUserLoginInfo(loginReqDTO);
if (!resp.isSuccess()) {
return resp;
}
BaseResponse<Object> response = new BaseResponse<>();
UserDTO user = (UserDTO) SecurityUtils.getSubject().getPrincipal();
loginLogUtil.saveLog(httpServletRequest, request, LoginType.LOGIN.getCode(), user);
response.setContent(user);
return response;
}
}catch (Exception e) {
throw new ApplicationException("登录失败:" + e.getMessage(), e);
}
throw new ApplicationException("登录失败");
}
private static final Boolean login(String account, String password, String host) throws Exception {
UsernamePasswordToken token = new UsernamePasswordToken(account, password, host);
token.setRememberMe(true);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
return subject.isAuthenticated();
} catch (LockedAccountException e) {
throw new LoginException(Resources.getMessage("ACCOUNT_LOCKED", token.getPrincipal()));
} catch (DisabledAccountException e) {
throw new LoginException(Resources.getMessage("ACCOUNT_DISABLED", token.getPrincipal()));
} catch (ExpiredCredentialsException e) {
throw new LoginException(Resources.getMessage("ACCOUNT_EXPIRED", token.getPrincipal()));
} catch (Exception e) {
throw new ApplicationException(e.getMessage(), e);
}
}
Realm.java
@Component
@Slf4j
public class Realm extends BaseRealm {
@Reference(version = "${sys.service.version}")
private SystemUserFacade systemUserFacade;
@Reference(version = "${sys.service.version}")
private SystemSetsOfBooksUserFacade systemSetsOfBooksUserFacade;
@Override
public boolean supports(AuthenticationToken token) {
return token != null && token instanceof UsernamePasswordToken;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
if (null == principals || null == principals.getPrimaryPrincipal()) {
return null;
}
SimpleAuthorizationInfo info = ShiroUtil.getAuthorizationInfo(principals, this.sessionDAO);
if (info == null) {
throw new AccountException("授权失败!");
}
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
throws AuthenticationException {
log.info("调用Realm ,doGetAuthenticationInfo:");
LoginIdReqDTO mdpSecUserSearchParam = new LoginIdReqDTO();
String loginId = (String) authcToken.getPrincipal();
mdpSecUserSearchParam.setLoginId(loginId);
log.info("登录loginId:" + loginId);
BaseResponse<SystemUserPwdResponseDTO> userResponse = systemUserFacade.findUserPwdByLoginId(mdpSecUserSearchParam);
log.info("登录返回:" + userResponse.toString());
SystemUserPwdResponseDTO user = userResponse.getContent();
if (null == user) {
throw new AccountException("无法通过账号获取到用户数据!");
}
log.debug("密码:" + user.getPassword() + "_" + String.valueOf((char[]) authcToken.getCredentials()));
if (null != user && TrueOrFalseEnum.FALSE.getValue().equals(user.getUsable())) {
throw new AccountException("用户已被禁用!");
} else if (null != user && !user.getPassword().equals(String.valueOf((char[]) authcToken.getCredentials()))) {
throw new AccountException("用户名或密码错误!");
} else {
log.info(user.getName() + ",用户登录成功:" + user.getLoginId());
}
UserDTO userDTO = new UserDTO();
userDTO.setUserId(user.getUserId());
userDTO.setSetsOfBooksId(-1L);
userDTO.setName(user.getName());
userDTO.setLoginId(user.getLoginId());
userDTO.setTenantId(user.getTenantId());
userDTO.setMobileTelephone(user.getMobileTelephone());
Subject currentUser = SecurityUtils.getSubject();
if (null != currentUser) {
super.clearCache(currentUser.getPrincipals());
Session session = currentUser.getSession();
userDTO.setSessionId(session.getId().toString());
}
return new SimpleAuthenticationInfo(userDTO, user.getPassword(), getName());
}
@Override
protected void assertCredentialsMatch(AuthenticationToken authcToken,
AuthenticationInfo info) throws AuthenticationException {
return;
}
}
二、短信验证码登陆 如果有另外一种登陆方式,需要重新定义 realm,token MyShiroConfig,项目启动时调用
@Configuration
@Slf4j
public class MyShiroConfig {
@Autowired
private Realm realm;
@Autowired
private BaseRealm baseRealm;
@Autowired
private PhoneMessageRealm phoneMessageRealm;
@Bean
public Authenticator authenticator() {
ModularRealmAuthenticator authenticator = new CustomModularRealmAuthenticator();
authenticator.setRealms(Arrays.asList(realm, baseRealm,phoneMessageRealm));
authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
DefaultWebSecurityManager manager = SpringContextUtil.getBean(DefaultWebSecurityManager.class);
manager.setAuthenticator(authenticator);
return authenticator;
}
}
解决多个realm覆盖问题 自定义ModularRealmAuthenticator,解决多个realm之后异常覆盖的问题
@Slf4j
public class CustomModularRealmAuthenticator extends ModularRealmAuthenticator {
@Override
protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) {
AuthenticationStrategy strategy = getAuthenticationStrategy();
AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token);
if (log.isTraceEnabled()) {
log.trace("Iterating through {} realms for PAM authentication", realms.size());
}
AuthenticationException authenticationException = null;
for (Realm realm : realms) {
aggregate = strategy.beforeAttempt(realm, token, aggregate);
if (realm.supports(token)) {
log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm);
AuthenticationInfo info = null;
Throwable t = null;
try {
info = realm.getAuthenticationInfo(token);
} catch (Throwable throwable) {
t = throwable;
if (throwable instanceof AuthenticationException) {
authenticationException = (AuthenticationException) throwable;
}
if (log.isDebugEnabled()) {
String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:";
log.debug(msg, t);
}
}
if (authenticationException != null) {
throw authenticationException;
}
aggregate = strategy.afterAttempt(realm, token, info, aggregate, t);
} else {
log.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token);
}
}
aggregate = strategy.afterAllAttempts(token, aggregate);
return aggregate;
}
}
第二个 短信验证码登陆 登陆功能
@PostMapping("/plogin")
@ApiOperation(value = "通过短信登陆")
public Object loginByMessage(@RequestParam("code") String messCode,@RequestParam("phone") String phone,HttpServletRequest request) throws LoginException {
try {
if (messCode == null || messCode.equals("")) {
throw new ApplicationException("验证码不能为空");
}
Subject subject = SecurityUtils.getSubject();
if(phoneLoing(messCode, phone, subject)) {
LoginReqDTO loginReqDTO = new LoginReqDTO();
loginReqDTO.setLastLoginIp(getIpAddr(request));
loginReqDTO.setLastLoginTime(LocalDateTime.now());
BaseResponse<Long> resp = systemUserFacade.updateUserLoginInfo(loginReqDTO);
if (!resp.isSuccess()) {
return resp;
}
BaseResponse<Object> response = new BaseResponse<>();
UserDTO user = (UserDTO) SecurityUtils.getSubject().getPrincipal();
VisitLoginReqDTO visitLoginReqDTO = loginReqDTO.convertTo(VisitLoginReqDTO.class);
loginLogUtil.saveLog(request, visitLoginReqDTO, LoginType.LOGIN.getCode(), user);
response.setContent(user);
return response;
}
} finally {
}
throw new ApplicationException("登录失败");
}
private boolean phoneLoing(String messCode, String phone, Subject subject) throws LoginException {
UserToken token = new UserToken(phone, messCode);
token.setRememberMe(true);
try {
subject.login(token);
return subject.isAuthenticated();
} catch (LockedAccountException e) {
throw new LoginException(Resources.getMessage("ACCOUNT_LOCKED", token.getPrincipal()));
} catch (DisabledAccountException e) {
throw new LoginException(Resources.getMessage("ACCOUNT_DISABLED", token.getPrincipal()));
} catch (ExpiredCredentialsException e) {
throw new LoginException(Resources.getMessage("ACCOUNT_EXPIRED", token.getPrincipal()));
} catch (Exception e) {
throw new ApplicationException(e.getMessage(), e);
}
}
自定义UserToken
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserToken implements AuthenticationToken {
private String mobile;
private String messCode;
private boolean rememberMe = false;
public UserToken(String mobile,String messCode) {
this.mobile = mobile;
this.messCode= messCode;
}
@Override
public Object getPrincipal() {
return getMobile();
}
@Override
public Object getCredentials() {
return getMessCode();
}
public void setRememberMe(boolean b) {
this.rememberMe = rememberMe;
}
}
自定义realm PhoneMessageRealm 权限校验器
@Component
@Slf4j
public class PhoneMessageRealm extends BaseRealm {
@Reference(version = "${sys.service.version}")
private SystemUserFacade systemUserFacade
@Reference(version = "${sys.service.version}")
private SystemSetsOfBooksUserFacade systemSetsOfBooksUserFacade;
@Reference(check = false,version = "${archives.service.version}")
ArchivesSalesmanFacade archivesSalesmanFacade;
@Autowired
RedisTemplate<String,Object> redisTemplate;
@Override
public boolean supports(AuthenticationToken token) {
return token != null && token instanceof UserToken;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
if (null == principals || null == principals.getPrimaryPrincipal()) {
return null;
}
SimpleAuthorizationInfo info = ShiroUtil.getAuthorizationInfo(principals, this.sessionDAO);
if (info == null) {
throw new AccountException("授权失败!");
}
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
throws AuthenticationException {
log.info("调用Realm ,doGetAuthenticationInfo:");
LoginIdReqDTO mdpSecUserSearchParam = new LoginIdReqDTO();
String mobile = (String) authcToken.getPrincipal();
String messCode = (String) authcToken.getCredentials();
SystemUserReqDTO systemUserReqDTO = new SystemUserReqDTO();
systemUserReqDTO.setMobileTelephone(mobile);
BaseResponse<SystemUserPwdResponseDTO> userResponse = systemUserFacade.getUserLoginByMobile(systemUserReqDTO);
log.info("登录返回:" + userResponse.toString());
SystemUserPwdResponseDTO user = userResponse.getContent();
if (null == user) {
throw new AccountException("无法通过手机号获取到用户数据!");
}
if (null != user && TrueOrFalseEnum.FALSE.getValue().equals(user.getUsable())) {
throw new AccountException("用户已被禁用!");
}
String key = MD5Util.getMD5Code(mobile + MobileConstant.MOBILE_ASSISTANT_KEY);
String value = (String)redisTemplate.opsForValue().get(key);
if (null == value) {
throw new ApplicationException("验证码不存在,或已失效");
}
if (!value.equals(messCode)) {
throw new ApplicationException("验证码错误");
} else {
log.info(user.getName() + ",用户登录成功:" + user.getLoginId());
}
UserDTO userDTO = new UserDTO();
userDTO.setUserId(user.getUserId());
userDTO.setSetsOfBooksId(-1L);
userDTO.setName(user.getName());
userDTO.setLoginId(user.getLoginId());
userDTO.setTenantId(user.getTenantId());
userDTO.setMobileTelephone(user.getMobileTelephone());
Subject currentUser = SecurityUtils.getSubject();
if (null != currentUser) {
super.clearCache(currentUser.getPrincipals());
Session session = currentUser.getSession();
userDTO.setSessionId(session.getId().toString());
}
return new SimpleAuthenticationInfo(userDTO, user.getPassword(), getName());
}
@Override
protected void assertCredentialsMatch(AuthenticationToken authcToken,
AuthenticationInfo info) throws AuthenticationException {
return;
}
}
|