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에 따라 라이센스가 부여됩니다.