SpringBoot+Shiro 학습 의 암호 화 및 로그 인 실패 횟수 제한 예시
전편 사고 문제 해결
전편 에서 저 희 는 같은 계 정의 로그 인 인원수 제한 shiro 차단기 의 작성 만 완 료 했 습 니 다.사용 자 를 수 동 으로 차 는 기능 에 대해 서 는 session 필드 에 key 를 kickout 으로 추가 하 는 불 값 을 사용 하 였 습 니 다.이전에 작 성 된 KickoutSession Control Filter 차단기 로 사용 자 를 차 는 지 여 부 를 판단 하 였 을 뿐 현재 온라인 사용자 목록 의 핵심 코드 를 어떻게 가 져 오 는 지 는 말 하지 않 았 습 니 다.아래 에 붙 이기:
/**
* <p>
*
* </p>
*
* @author z77z
* @since 2017-02-10
*/
@Service
public class SysUserService extends ServiceImpl<SysUserMapper, SysUser> {
@Autowired
RedisSessionDAO redisSessionDAO;
public Page<UserOnlineBo> getPagePlus(FrontPage<UserOnlineBo> frontPage) {
// redis shiro session Dao, shiro+redis
// spring redisSessionDAO
// session .
Collection<Session> sessions = redisSessionDAO.getActiveSessions();
Iterator<Session> it = sessions.iterator();
List<UserOnlineBo> onlineUserList = new ArrayList<UserOnlineBo>();
Page<UserOnlineBo> pageList = frontPage.getPagePlus();
// session
while (it.hasNext()) {
// shiro session
//
Session session = it.next();
//
Object obj = session.getAttribute("kickout");
if (obj != null)
continue;
UserOnlineBo onlineUser = getSessionBo(session);
onlineUserList.add(onlineUser);
}
// List<UserOnlineBo> mybatisPlus page
int page = frontPage.getPage() - 1;
int rows = frontPage.getRows() - 1;
int startIndex = page * rows;
int endIndex = (page * rows) + rows;
int size = onlineUserList.size();
if (endIndex > size) {
endIndex = size;
}
pageList.setRecords(onlineUserList.subList(startIndex, endIndex));
pageList.setTotal(size);
return pageList;
}
// session UserOnline
private UserOnlineBo getSessionBo(Session session){
// session 。
Object obj = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
if(null == obj){
return null;
}
// SimplePrincipalCollection 。
if(obj instanceof SimplePrincipalCollection){
SimplePrincipalCollection spc = (SimplePrincipalCollection)obj;
/**
* ,@link SampleRealm.doGetAuthenticationInfo(...)
* return new SimpleAuthenticationInfo(user,user.getPswd(), getName()); user 。
*/
obj = spc.getPrimaryPrincipal();
if(null != obj && obj instanceof SysUser){
// session + user
UserOnlineBo userBo = new UserOnlineBo((SysUser)obj);
//
userBo.setLastAccess(session.getLastAccessTime());
// ip
userBo.setHost(session.getHost());
//session ID
userBo.setSessionId(session.getId().toString());
//session
userBo.setLastLoginTime(session.getLastAccessTime());
// ttl(ms)
userBo.setTimeout(session.getTimeout());
//session
userBo.setStartTime(session.getStartTimestamp());
//
userBo.setSessionStatus(false);
return userBo;
}
}
return null;
}
}
코드 에 주석 이 비교적 완벽 하고 소스 코드 를 다운로드 하여 볼 수도 있 습 니 다.이렇게 결합 해서 보면 이해 하기 쉽 고 모 르 는 것 은 댓 글 에 메 시 지 를 남기 면 반드시 돌아 갑 니 다!Ajax 요청 에 대한 최적화:여기 에는 전제 가 있 습 니 다.저 희 는 Ajax 가 페이지 redirect 와 forward 점프 를 할 수 없다 는 것 을 알 고 있 습 니 다.그래서 Ajax 요청 이 로그 인하 지 않 았 다 면 이 요청 은 사용자 에 게 아무런 반응 이 없 는 것 처럼 느껴 집 니 다.사용 자 는 사용자 가 이미 종료 되 었 는 지 모 릅 니 다.즉,KickoutSession Control Filter 차단기 가 차단 되면 정상적으로 차 이면 차 인 알림 페이지 로 넘 어 갑 니 다.Ajax 요청 이 라면 사용자 에 게 감각 이 없다 는 느낌 을 줍 니 다.핵심 해결 코드 는 다음 과 같 습 니 다.
Map<String, String> resultMap = new HashMap<String, String>();
// Ajax
if ("XMLHttpRequest".equalsIgnoreCase(((HttpServletRequest) request).getHeader("X-Requested-With"))) {
resultMap.put("user_status", "300");
resultMap.put("message", " , !");
// json
out(response, resultMap);
}else{
//
WebUtils.issueRedirect(request, response, kickoutUrl);
}
private void out(ServletResponse hresponse, Map<String, String> resultMap)
throws IOException {
try {
hresponse.setCharacterEncoding("UTF-8");
PrintWriter out = hresponse.getWriter();
out.println(JSON.toJSONString(resultMap));
out.flush();
out.close();
} catch (Exception e) {
System.err.println("KickoutSessionFilter.class JSON , 。");
}
}
이것 은 KickoutSession Control Filter 라 는 차단기 에서 수정 한 것 입 니 다.목표:
shiro 자 체 는 암호 화 를 실현 하고 PasswordService 와 Credential Matcher 를 제공 하여 암호 화 암호 화 암호 화 및 암호 화 서 비 스 를 제공 합 니 다.
제 가 바로 EDS 암호 화 를 실 현 했 고 저 장 된 암호 화 명문 은 password+username 방식 으로 비밀 번 호 를 줄 였 습 니 다.똑 같 고 비밀문서 도 똑 같은 문제 입 니 다.여기 서 저 는'EDS 의 암호 화 복호화 코드'를 붙 였 을 뿐 입 니 다.그리고 저 는 MyShiroRealm 파일 도 바 꾸 었 습 니 다.라 이브 러 리 를 조사 할 때 암호 화 한 다음 에 확인 하 겠 습 니 다.또한 사용 자 를 만 들 때 잊 지 말 아야 할 암호 화 를 데이터베이스 에 저장 합 니 다.여기 보조 코드 입 니 다.
/**
* DES
*
* @author z77z
* @datetime 2017-3-13
*/
public class MyDES {
/**
* DES
*/
private static final byte[] DES_KEY = { 21, 1, -110, 82, -32, -85, -128, -65 };
/**
* , (DES)
*
* @param data
*
* @return
*/
@SuppressWarnings("restriction")
public static String encryptBasedDes(String data) {
String encryptedData = null;
try {
// DES
SecureRandom sr = new SecureRandom();
DESKeySpec deskey = new DESKeySpec(DES_KEY);
// , DESKeySpec SecretKey
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey key = keyFactory.generateSecret(deskey);
//
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key, sr);
// ,
encryptedData = new sun.misc.BASE64Encoder().encode(cipher.doFinal(data.getBytes()));
} catch (Exception e) {
// log.error(" , :", e);
throw new RuntimeException(" , :", e);
}
return encryptedData;
}
/**
* , (DES)
*
* @param cryptData
*
* @return
*/
@SuppressWarnings("restriction")
public static String decryptBasedDes(String cryptData) {
String decryptedData = null;
try {
// DES
SecureRandom sr = new SecureRandom();
DESKeySpec deskey = new DESKeySpec(DES_KEY);
// , DESKeySpec SecretKey
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey key = keyFactory.generateSecret(deskey);
//
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, key, sr);
// ,
decryptedData = new String(cipher.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(cryptData)));
} catch (Exception e) {
// log.error(" , :", e);
throw new RuntimeException(" , :", e);
}
return decryptedData;
}
public static void main(String[] args) {
String str = "123456";
// DES
String s1 = encryptBasedDes(str);
System.out.println(s1);
// DES
String s2 = decryptBasedDes(s1);
System.err.println(s2);
}
}
목표 달성1 시간 동안 비밀 번 호 를 최대 5 번 까지 재 시도 하고 시도 횟수 가 5 번 을 넘 으 면 1 시간 동안 잠 그 고 1 시간 후에 다시 시도 할 수 있 으 며,재 시도 에 실패 하면 하루 처럼 잠 그 는 것 으로 유추 해 암호 가 폭력 적 으로 풀 리 지 않도록 할 수 있다.저 희 는 redis 데이터 베 이 스 를 사용 하여 현재 사용자 의 로그 인 횟수 를 저장 합 니 다.즉,인증 방법 을 실행 하 는 것 입 니 다.
MyShiroRealm.do GetAuthenticationInfo()의 횟수 는 로그 인 에 성공 하면 계 수 를 비 웁 니 다.초과 하면 해당 오류 정 보 를 되 돌려 줍 니 다.(redis 의 구체 적 인 조작 은 이전 spring boot+redis 의 블 로 그 를 볼 수 있 습 니 다)이 논리 에 따라 MyShiroRealm.자바 를 다음 과 같이 수정 할 수 있 습 니 다.
/**
* .( ) : Authentication
*
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authcToken) throws AuthenticationException {
System.out.println(" :MyShiroRealm.doGetAuthenticationInfo()");
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
String name = token.getUsername();
String password = String.valueOf(token.getPassword());
// ,
ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
opsForValue.increment(SHIRO_LOGIN_COUNT+name, 1);
// 5 ,
if(Integer.parseInt(opsForValue.get(SHIRO_LOGIN_COUNT+name))>=5){
opsForValue.set(SHIRO_IS_LOCK+name, "LOCK");
stringRedisTemplate.expire(SHIRO_IS_LOCK+name, 1, TimeUnit.HOURS);
}
if ("LOCK".equals(opsForValue.get(SHIRO_IS_LOCK+name))){
throw new DisabledAccountException(" 5 , !");
}
Map<String, Object> map = new HashMap<String, Object>();
map.put("nickname", name);
// password+name
String paw = password+name;
String pawDES = MyDES.encryptBasedDes(paw);
map.put("pswd", pawDES);
SysUser user = null;
//
List<SysUser> userList = sysUserService.selectByMap(map);
if(userList.size()!=0){
user = userList.get(0);
}
if (null == user) {
throw new AccountException(" !");
}else if(user.getStatus()==0){
/**
* status 。 <code>DisabledAccountException</code>
*/
throw new DisabledAccountException(" !");
}else{
//
// last login time
user.setLastLoginTime(new Date());
sysUserService.updateById(user);
//
opsForValue.set(SHIRO_LOGIN_COUNT+name, "0");
}
return new SimpleAuthenticationInfo(user, password, getName());
}
demo 다운로드 주소:springboot_mybatisplus_jb51.rar이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
[MeU] Hashtag 기능 개발➡️ 기존 Tag 테이블에 존재하지 않는 해시태그라면 Tag , tagPostMapping 테이블에 모두 추가 ➡️ 기존에 존재하는 해시태그라면, tagPostMapping 테이블에만 추가 이후에 개발할 태그 기반 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.